diff --git a/modules/common/biometrics/manifest.v1.json b/modules/common/biometrics/manifest.v1.json index fdef253c..b4b8d238 100644 --- a/modules/common/biometrics/manifest.v1.json +++ b/modules/common/biometrics/manifest.v1.json @@ -1,6 +1,6 @@ { "id": "sleepypod-biometric-bundle", - "version": "1.0.0", + "version": "1.1.0", "schemaVersion": 1, "canonicalization": "finite-binary64-json-v1", "sampleRateHz": 500, @@ -30,11 +30,20 @@ "default": 5, "unit": "windows", "description": "Number of accepted heart-rate estimates retained by the tracker." + }, + "minimumPeakScore": { + "type": "number", + "minimum": 0.1, + "maximum": 0.8, + "default": 0.4, + "unit": "score", + "description": "Minimum subharmonic-summation autocorrelation score for a heart-rate estimate. Weaker windows report no heart rate instead of a guess; raising it trades coverage for accuracy." } }, "required": [ "trackingToleranceBpm", - "trackingHistoryLength" + "trackingHistoryLength", + "minimumPeakScore" ], "additionalProperties": false }, @@ -375,7 +384,8 @@ "defaults": { "heart_rate": { "trackingToleranceBpm": 15, - "trackingHistoryLength": 5 + "trackingHistoryLength": 5, + "minimumPeakScore": 0.4 }, "breathing_rate": { "intervalLowerRatio": 0.5, @@ -624,7 +634,7 @@ } }, "sourceHashes": { - "modules/common/biometrics/signal.py": "fff73469815673c8daebba9e2c9e7bcf97708939ad7a5639a52f0a53c9d75c18", + "modules/common/biometrics/signal.py": "0b1447c1162245232c053aeeb5cb3e75e7da3392c38a43c465f28596f084b32f", "modules/piezo-processor/main.py": "75db2ea2f005fd537d994b306332c1abf97c0c28abb496a8d3af03816ccf8550", "src/lib/sleep-stages.ts": "b1800e49ce8525d314333da6a29a63131a002d160bb294fa97b24df6e655e228", "modules/piezo-processor/runtime_adapter.py": "0a22a2fa05aae6569bfc4b53fba80d75e89f702f021ccb61f82ce64e284c5bb8", diff --git a/modules/common/biometrics/signal.py b/modules/common/biometrics/signal.py index dface3b0..58e103fe 100644 --- a/modules/common/biometrics/signal.py +++ b/modules/common/biometrics/signal.py @@ -27,6 +27,7 @@ class ProcessingParameters: breathing_interval_upper_ratio: float hrv_harmonic_tolerance: float hrv_minimum_windows: int + heart_rate_minimum_peak_score: float def __post_init__(self): resolve_config([ @@ -36,6 +37,7 @@ def __post_init__(self): {"path": "breathing_rate.intervalUpperRatio", "value": self.breathing_interval_upper_ratio}, {"path": "hrv_index.harmonicTolerance", "value": self.hrv_harmonic_tolerance}, {"path": "hrv_index.minimumWindows", "value": self.hrv_minimum_windows}, + {"path": "heart_rate.minimumPeakScore", "value": self.heart_rate_minimum_peak_score}, ]) @classmethod @@ -46,7 +48,8 @@ def from_config(cls, config): config["breathing_rate"]["intervalLowerRatio"], config["breathing_rate"]["intervalUpperRatio"], config["hrv_index"]["harmonicTolerance"], - config["hrv_index"]["minimumWindows"]) + config["hrv_index"]["minimumWindows"], + config["heart_rate"]["minimumPeakScore"]) DEFAULT_PARAMETERS = ProcessingParameters.from_config(manifest()["defaults"]) @@ -111,7 +114,8 @@ def _compute_autocorr(filtered: np.ndarray, fs: float) -> Optional[np.ndarray]: def subharmonic_summation_hr(samples: np.ndarray, fs: float = SAMPLE_RATE, bpm_range: tuple = (45, 120), - n_harmonics: int = 3): + n_harmonics: int = 3, + min_score: float = DEFAULT_PARAMETERS.heart_rate_minimum_peak_score): """Subharmonic summation for robust fundamental HR detection. Only scores autocorrelation PEAKS (not every lag) to avoid edge effects @@ -120,7 +124,10 @@ def subharmonic_summation_hr(samples: np.ndarray, fs: float = SAMPLE_RATE, Hermes 1988 (JASA), adapted for BCG by Bruser et al. 2011. - Returns (heart_rate_bpm, score) or (None, 0.0) on failure. + Returns (heart_rate_bpm, score) or (None, 0.0) on failure. A best score + below min_score also returns (None, score): on a weak heartbeat the tallest + autocorrelation peak is usually not the heart (the true rate is often not a + candidate at all), so reporting nothing beats reporting a confident guess. """ filtered = _bandpass(samples, HR_BAND[0], HR_BAND[1], fs) acr = _compute_autocorr(filtered, fs) @@ -161,7 +168,7 @@ def subharmonic_summation_hr(samples: np.ndarray, fs: float = SAMPLE_RATE, best_score = score best_lag = lag - if best_lag is None or best_score < 0.1: + if best_lag is None or best_score < min_score: return None, best_score hr = 60.0 * fs / best_lag @@ -483,7 +490,7 @@ def estimate(self, timestamp_s: float, hr_samples: np.ndarray, # Match live warmup exactly; never pad unavailable history with zeroes. if len(hr_samples) < int(10 * fs): return WindowEstimate(timestamp_s, None, None, None, None, 0.0, ("hr_warmup",)) - hr_raw, hr_score = subharmonic_summation_hr(hr_samples, fs) + hr_raw, hr_score = subharmonic_summation_hr(hr_samples, fs, min_score=self.parameters.heart_rate_minimum_peak_score) hr = self.hr_tracker.update(hr_raw, hr_score) br = compute_breathing_rate(breathing_samples, fs, self.parameters) hrv = (compute_hrv(hrv_samples, fs, self.parameters) diff --git a/modules/piezo-processor/test_shared_signal.py b/modules/piezo-processor/test_shared_signal.py index c5d05d8c..6b42ea8f 100644 --- a/modules/piezo-processor/test_shared_signal.py +++ b/modules/piezo-processor/test_shared_signal.py @@ -77,3 +77,26 @@ def test_explicit_event_time_and_warmup_never_consult_wall_clock(): estimator.estimate(20, data, data, data, fs=250) with pytest.raises(ValueError, match="finite"): estimator.estimate(float("nan"), data, data, data) + + +def test_minimum_peak_score_abstains_on_weak_windows_only(): + # A heartbeat buried in noise still yields a tallest peak, just a weak one. + weak = samples()[-15000:] + np.random.default_rng(7).normal(0, 2.5, 15000) + hr, score = core.subharmonic_summation_hr(weak) + assert hr is not None and 0.1 < score < 0.8 + assert core.subharmonic_summation_hr(weak, min_score=score - 1e-6)[0] == pytest.approx(hr) + assert core.subharmonic_summation_hr(weak, min_score=score + 1e-6) == (None, pytest.approx(score)) + strict = core.WindowEstimator(parameters("heart_rate.minimumPeakScore", score + 1e-6)) + estimate = strict.estimate(1700000000, weak, samples()[-30000:], samples()) + assert estimate.heart_rate is None and "no_hr" in estimate.rejection_reasons + # 0.1 is the previous fixed floor; the shipped default is now 0.4. + legacy = core.WindowEstimator(parameters("heart_rate.minimumPeakScore", 0.1)) + assert legacy.estimate(1700000000, weak, samples()[-30000:], samples()).heart_rate == pytest.approx(hr) + assert core.DEFAULT_PARAMETERS.heart_rate_minimum_peak_score == 0.4 + assert core.subharmonic_summation_hr(weak)[0] == (hr if score >= 0.4 else None) + + +@pytest.mark.parametrize("value", [0.05, 0.85]) +def test_minimum_peak_score_range_is_validated(value): + with pytest.raises(ValueError): + parameters("heart_rate.minimumPeakScore", value) diff --git a/scripts/biometrics-local-fit/README.md b/scripts/biometrics-local-fit/README.md index e61d5bc7..bb2029cc 100644 --- a/scripts/biometrics-local-fit/README.md +++ b/scripts/biometrics-local-fit/README.md @@ -68,8 +68,14 @@ Outputs: - `out//.json`: per-night HR, breathing and HRV error, plus the full evaluator report. A metric without paired reference data reads `no_paired_reference`, never zero error. -- `out/ranking.json` and `out/ranking.md`: ranked by the tuning split's - equal-night mean HR MAE difference from defaults, with validation alongside. +- `out/ranking.json` and `out/ranking.md`: ranked by tuning HR error on each + candidate's own readings, among candidates that keep at least `--min-coverage` + (default 75%) of the defaults' Watch coverage; validation and holdout are shown + alongside. Each split also shows the common-support difference against + defaults. That difference reads 0 for a change that only withholds readings + (such as `heart_rate.minimumPeakScore`), which is why it is not the ranking key. +- `--save-readings` also writes every replay's derived readings to + `out/readings/-.json` for error analysis. - `out/run.json`: timing, worker count and the Python build identity. ## 4. Check parity, then confirm on the pod diff --git a/scripts/biometrics-local-fit/cli.ts b/scripts/biometrics-local-fit/cli.ts index 6c02cfa7..f26a8f40 100644 --- a/scripts/biometrics-local-fit/cli.ts +++ b/scripts/biometrics-local-fit/cli.ts @@ -8,7 +8,7 @@ import { compareWithPod, parseLocalReport, rankCandidates, rankingMarkdown } fro const usage = `Usage: verify BUNDLE - fit BUNDLE CANDIDATES.json OUTPUT_DIR [--jobs N] [--splits tuning,validation] [--include-holdout] [--protocol FILE] [--python PATH] + fit BUNDLE CANDIDATES.json OUTPUT_DIR [--jobs N] [--splits tuning,validation] [--include-holdout] [--save-readings] [--min-coverage 0.75] [--protocol FILE] [--python PATH] sweep BUNDLE GRID.json|default OUTPUT_DIR [same options as fit] parity OUTPUT_DIR POD_RESULT.json [POD_RESULT.json ...] BUNDLE is the tar from GET /api/biometric-tuning/datasets/{id}/export, or a directory extracted from it.` @@ -17,7 +17,7 @@ function options(args: string[]) { const values: Record = {} const positional: string[] = [] for (let index = 0; index < args.length; index++) { - if (args[index] === '--include-holdout') values['include-holdout'] = 'true' + if (args[index] === '--include-holdout' || args[index] === '--save-readings') values[args[index].slice(2)] = 'true' else if (args[index].startsWith('--')) values[args[index].slice(2)] = args[++index] ?? '' else positional.push(args[index]) } @@ -43,6 +43,7 @@ async function fit(bundlePath: string, candidates: Candidate[], outputDirectory: const splits = (flags.splits?.split(',') ?? [...LOCAL_SPLITS, ...(includeHoldout ? ['holdout'] : [])]) as LocalSplit[] const started = performance.now() const result = await fitCandidates({ bundle, candidates, outputDirectory, splits, includeHoldout, + readingsDirectory: flags['save-readings'] === 'true' ? join(outputDirectory, 'readings') : undefined, jobs: flags.jobs ? Number(flags.jobs) : undefined, protocol: flags.protocol ? await readJson(flags.protocol) as never : undefined, python: flags.python ? defaultPythonLocation(flags.python) : undefined, @@ -51,7 +52,7 @@ async function fit(bundlePath: string, candidates: Candidate[], outputDirectory: await mkdir(join(outputDirectory, report.candidate.id), { recursive: true }) await writeFile(join(outputDirectory, report.candidate.id, `${report.split}.json`), `${JSON.stringify(report)}\n`) } - const ranking = rankCandidates(result.reports) + const ranking = rankCandidates(result.reports, flags['min-coverage'] ? Number(flags['min-coverage']) : undefined) await writeFile(join(outputDirectory, 'ranking.json'), `${JSON.stringify(ranking, null, 2)}\n`) await writeFile(join(outputDirectory, 'ranking.md'), rankingMarkdown(ranking)) const totalWallMs = performance.now() - started diff --git a/scripts/biometrics-local-fit/fit.ts b/scripts/biometrics-local-fit/fit.ts index b7d3bf83..4087cb6e 100644 --- a/scripts/biometrics-local-fit/fit.ts +++ b/scripts/biometrics-local-fit/fit.ts @@ -96,6 +96,8 @@ export interface FitOptions { splits?: readonly LocalSplit[] /** Score holdout nights too, once, for a candidate that is already chosen. */ includeHoldout?: boolean + /** Also write each replay's derived readings here, one JSON file per night and configuration. */ + readingsDirectory?: string jobs?: number protocol?: z.input python?: PythonLocation @@ -145,6 +147,11 @@ export async function fitCandidates(options: FitOptions) { { version: 'retained-waveform-work-v1', input, configs: { [snapshot.side]: resolved, [otherSideSide]: defaults.resolved } }, { timeoutMs: 3600000, expectedBuildHash: build.hash }) outputs.set(`${item.cohortId}:${item.hash}`, { cohortId: item.cohortId, configHash: item.hash, ...output }) + if (options.readingsDirectory) { + await mkdir(options.readingsDirectory, { recursive: true }) + await writeFile(join(options.readingsDirectory, `${item.cohortId}-${item.hash.slice(0, 12)}.json`), + `${JSON.stringify({ cohortId: item.cohortId, configHash: item.hash, side: snapshot.side, readings: output.readings })}\n`) + } options.onProgress?.(++done, items.length, { cohortId: item.cohortId, candidateIds: candidates.filter(row => row.resolvedHash === item.hash).map(row => row.id), wallMs: performance.now() - itemStarted }) }) diff --git a/scripts/biometrics-local-fit/report.test.ts b/scripts/biometrics-local-fit/report.test.ts index 05d3b5f6..a80df06f 100644 --- a/scripts/biometrics-local-fit/report.test.ts +++ b/scripts/biometrics-local-fit/report.test.ts @@ -24,7 +24,7 @@ afterAll(() => { }) /** Score a candidate with fixed synthetic readings (references are 62 bpm). */ -function localReport(id: string, overrides: Array<{ path: string, value: unknown }>, split: 'tuning' | 'validation' | 'holdout', heartRates: { default: number, candidate: number }) { +function localReport(id: string, overrides: Array<{ path: string, value: unknown }>, split: 'tuning' | 'validation' | 'holdout', heartRates: { default: number | Array, candidate: number | Array }) { const defaults = { manifestHash, resolvedHash: configHash(defaultConfig), resolved: defaultConfig } const candidate = resolveConfig(overrides as never) const configs = [{ label: 'default' as const, revisionId: null, ...defaults }, @@ -32,9 +32,10 @@ function localReport(id: string, overrides: Array<{ path: string, value: unknown const night = exportNights.find(row => row.split === split) ?? exportNights[0] const artifact = fixture.snapshot.version === 'waveform-dataset-v1' ? fixture.snapshot.waveformInputs.find(row => row.cohortId === night.cohortId)?.input.artifacts[0].artifact.sha256 ?? '' : '' const outputs = [...new Map(configs.map(config => [config.resolvedHash, config])).values()].map((config) => { - const heartRate = config.label === 'default' ? heartRates.default : heartRates.candidate - const readings = Array.from({ length: 10 }, (_, index) => ({ side: 'left' as const, timestampMs: night.startMs + index * 60000, heartRate, hrvIndex: 30, - hrvMetric: 'window_ibi_variability_v1' as const, breathingRate: 14, qualityScore: 1, flags: null, hrRaw: heartRate, sourceSide: 'left' as const, + const chosen = config.label === 'default' ? heartRates.default : heartRates.candidate + const rate = (index: number) => Array.isArray(chosen) ? chosen[index] : chosen + const readings = Array.from({ length: 10 }, (_, index) => ({ side: 'left' as const, timestampMs: night.startMs + index * 60000, heartRate: rate(index), hrvIndex: 30, + hrvMetric: 'window_ibi_variability_v1' as const, breathingRate: 14, qualityScore: 1, flags: null, hrRaw: rate(index), sourceSide: 'left' as const, configHash: config.resolvedHash, limitations: [] })) return { cohortId: night.cohortId, configHash: config.resolvedHash, readings, run: { version: 'retained-waveform-run-v1' as const, build_hash: pythonBuildHash, decoded_records: 10, raw_records: 10, counts: { records: 10, accepted_frames: 10, empty_channel_frames: 0, pump_gated_frames: 0, preroll_outputs: 0, scored_outputs: readings.length }, @@ -66,7 +67,7 @@ describe('local fit reports', () => { expect(() => parseLocalReport({ ...report, label: 'pod' })).toThrow() }) - it('ranks by tuning HR error difference and keeps validation alongside', () => { + it('ranks by tuning HR error on own readings and keeps validation alongside', () => { const better = [{ path: 'heart_rate.trackingToleranceBpm', value: 10 }], worse = [{ path: 'heart_rate.trackingToleranceBpm', value: 25 }] const reports = [ localReport('worse', worse, 'tuning', { default: 60, candidate: 58 }), localReport('worse', worse, 'validation', { default: 60, candidate: 59 }), @@ -74,14 +75,37 @@ describe('local fit reports', () => { localReport('defaults', [], 'tuning', { default: 60, candidate: 60 }), localReport('defaults', [], 'validation', { default: 60, candidate: 60 }), ] const ranking = rankCandidates(reports) - expect(ranking).toMatchObject({ label: 'exploratory-local', evidence: 'not_activation_evidence' }) + expect(ranking).toMatchObject({ label: 'exploratory-local', evidence: 'not_activation_evidence', minimumCoverage: 0.75 }) expect(ranking.rows.map(row => [row.rank, row.id])).toEqual([[1, 'better'], [2, 'defaults'], [3, 'worse']]) - expect(ranking.rows[0].heartRate).toEqual({ tuning: { maeDifference: -1.5, mae: 0.5, scoredNights: 1, coverageReducedNights: 0 }, - validation: { maeDifference: 0, mae: 2, scoredNights: 1, coverageReducedNights: 0 } }) + expect(ranking.rows[0].heartRate).toEqual({ + tuning: { maeDifference: -1.5, mae: 0.5, ownMae: 0.5, coverage: 1, scoredNights: 1, coverageReducedNights: 0 }, + validation: { maeDifference: 0, mae: 2, ownMae: 2, coverage: 1, scoredNights: 1, coverageReducedNights: 0 } }) + expect(rankingMarkdown(ranking)).toContain('| 1 | better | 0.500 | -1.500 | 100% | 2.000 | 0.000 | 100% | n/a | n/a |') const tied = rankCandidates([localReport('b-two', [...better, { path: 'heart_rate.trackingHistoryLength', value: 8 }], 'tuning', { default: 60, candidate: 61 }), localReport('a-one', [...better, { path: 'hrv_index.minimumWindows', value: 10 }], 'tuning', { default: 60, candidate: 61 })]) expect(tied.rows.map(row => [row.id, row.changedDefaults])).toEqual([['a-one', 1], ['b-two', 2]]) - expect(rankingMarkdown(ranking)).toContain('| 1 | better | 0.500 | -1.500 | 2.000 | 0.000 | 0/0 | n/a | n/a |') + }) + + it('credits abstaining on bad windows but not below the coverage floor', () => { + const gate = (value: number) => [{ path: 'heart_rate.minimumPeakScore', value }] + // References sit on readings 0, 2, 4, 6 and 8 at 62 bpm. Defaults are right + // except one wild reading; each gate only removes readings, never changes them. + const defaults = [62, 60, 62, 60, 62, 60, 62, 60, 90, 60] + const dropsWild = defaults.map((value, index) => index === 8 ? null : value) + const dropsMost = defaults.map((value, index) => index >= 2 && index % 2 === 0 ? null : value) + const ranking = rankCandidates([ + localReport('defaults', [], 'tuning', { default: defaults, candidate: defaults }), + localReport('gate-0.45', gate(0.45), 'tuning', { default: defaults, candidate: dropsWild }), + localReport('gate-0.6', gate(0.6), 'tuning', { default: defaults, candidate: dropsMost }), + ]) + const rows = Object.fromEntries(ranking.rows.map(row => [row.id, row])) + expect(rows.defaults.heartRate.tuning).toMatchObject({ ownMae: 5.6, coverage: 1 }) + expect(rows['gate-0.45'].heartRate.tuning).toMatchObject({ ownMae: 0, coverage: 0.8, maeDifference: 0 }) + expect(rows['gate-0.6'].heartRate.tuning).toMatchObject({ ownMae: 0, coverage: 0.2, maeDifference: 0 }) + // Common support scores both gates as no change; own error with the coverage floor does not. + expect(ranking.rows.map(row => row.id)).toEqual(['gate-0.45', 'defaults', 'gate-0.6']) + expect(rankCandidates([localReport('gate-0.6', gate(0.6), 'tuning', { default: defaults, candidate: dropsMost }), + localReport('defaults', [], 'tuning', { default: defaults, candidate: defaults })], 0.1).rows[0].id).toBe('gate-0.6') }) it('shows holdout error alongside only when holdout nights were scored', () => { @@ -89,8 +113,8 @@ describe('local fit reports', () => { const without = rankingMarkdown(rankCandidates([localReport('tol10', overrides, 'tuning', { default: 60, candidate: 61 })])) expect(without).not.toContain('holdout') const ranking = rankCandidates([localReport('tol10', overrides, 'tuning', { default: 60, candidate: 61 }), localReport('tol10', overrides, 'holdout', { default: 60, candidate: 62 })]) - expect(ranking.rows[0].heartRate.holdout).toEqual({ maeDifference: -2, mae: 0, scoredNights: 1, coverageReducedNights: 0 }) - expect(rankingMarkdown(ranking)).toContain('| 1 | tol10 | 1.000 | -1.000 | n/a | n/a | 0.000 | -2.000 |') + expect(ranking.rows[0].heartRate.holdout).toEqual({ maeDifference: -2, mae: 0, ownMae: 0, coverage: 1, scoredNights: 1, coverageReducedNights: 0 }) + expect(rankingMarkdown(ranking)).toContain('| 1 | tol10 | 1.000 | -1.000 | 100% | n/a | n/a | n/a | 0.000 | -2.000 | 100% |') }) it('compares local default HR error with pod results within 0.1 bpm', () => { diff --git a/scripts/biometrics-local-fit/report.ts b/scripts/biometrics-local-fit/report.ts index 6c4bea53..a68eb711 100644 --- a/scripts/biometrics-local-fit/report.ts +++ b/scripts/biometrics-local-fit/report.ts @@ -10,6 +10,31 @@ type NightSummary = ReturnType>[num const mean = (values: number[]) => values.length ? values.reduce((sum, value) => sum + value, 0) / values.length : null +interface OwnScore { pairedCount: number, metrics: { mae: number } | null } +interface NumericWindow { metrics: Array<{ metric: string, baseline: string, candidate: string, ownSupport: { baseline: OwnScore, candidate: OwnScore } }> } + +/** Pool each configuration's own-support error across a night's windows. Unlike + * common support, this credits a candidate that abstains on hard windows, so it + * is always reported with the Watch coverage it kept. */ +function ownSupportNights(report: EvaluatorReport, metric: Metric) { + return (report.nights as Array<{ cohortId: string, numericComparisons?: NumericWindow[] }>).map((night) => { + const pooled = { default: { paired: 0, error: 0 }, candidate: { paired: 0, error: 0 } } + for (const window of night.numericComparisons ?? []) { + const row = window.metrics.find(item => item.metric === metric && item.baseline === 'default' && item.candidate === 'candidate') + if (!row) continue + for (const [key, score] of [['default', row.ownSupport.baseline], ['candidate', row.ownSupport.candidate]] as const) { + if (score.metrics && score.pairedCount) { + pooled[key].paired += score.pairedCount + pooled[key].error += score.metrics.mae * score.pairedCount + } + } + } + const mae = (key: 'default' | 'candidate') => pooled[key].paired ? pooled[key].error / pooled[key].paired : null + return { cohortId: night.cohortId, defaultPairedCount: pooled.default.paired, candidatePairedCount: pooled.candidate.paired, + defaultMae: mae('default'), candidateMae: mae('candidate') } + }) +} + function metricSummary(report: EvaluatorReport, metric: Metric) { const summary = (report.numericNightSummary as NightSummary[] | undefined) ?.find(row => row.metric === metric && row.baseline === 'default' && row.candidate === 'candidate') @@ -17,10 +42,15 @@ function metricSummary(report: EvaluatorReport, metric: Metric) { defaultMae: night.commonSupport.baseline.mae, candidateMae: night.commonSupport.candidate.mae, maeDifference: night.commonMaeDifference, coverageReduced: night.coverageReduced, lostObservationTimestamps: night.lostObservationTimestamps })) const scored = nights.filter(night => night.maeDifference !== null) + const own = ownSupportNights(report, metric) + const ownScored = own.filter(night => night.defaultMae !== null && night.candidateMae !== null) + const defaultPairs = own.reduce((sum, night) => sum + night.defaultPairedCount, 0) return { status: scored.length ? 'scored' as const : 'no_paired_reference' as const, totalNights: nights.length, scoredNights: scored.length, coverageReducedNights: nights.filter(night => night.coverageReduced).length, equalNightMeanMae: { default: mean(scored.map(night => night.defaultMae as number)), candidate: mean(scored.map(night => night.candidateMae as number)) }, - equalNightMeanMaeDifference: summary?.equalNightMeanMaeDifference ?? null, nights } + equalNightMeanMaeDifference: summary?.equalNightMeanMaeDifference ?? null, nights, + ownSupport: { equalNightMeanMae: { default: mean(ownScored.map(night => night.defaultMae as number)), candidate: mean(ownScored.map(night => night.candidateMae as number)) }, + referenceCoverage: defaultPairs ? own.reduce((sum, night) => sum + night.candidatePairedCount, 0) / defaultPairs : null, nights: own } } } export type MetricSummary = ReturnType @@ -53,11 +83,12 @@ export function parseLocalReport(value: unknown) { /** Rank by the tuning split's equal-night mean HR error difference against * defaults on common support (lower is better), validation shown alongside. */ -export function rankCandidates(reports: LocalFitReport[]) { +export function rankCandidates(reports: LocalFitReport[], minimumCoverage = 0.75) { const ids = [...new Set(reports.map(report => report.candidate.id))] const pick = (id: string, split: 'tuning' | 'validation' | 'holdout') => reports.find(report => report.candidate.id === id && report.split === split) const view = (report: LocalFitReport | undefined, metric: Metric) => report ? { maeDifference: report.summary[metric].equalNightMeanMaeDifference, mae: report.summary[metric].equalNightMeanMae.candidate, + ownMae: report.summary[metric].ownSupport?.equalNightMeanMae.candidate ?? null, coverage: report.summary[metric].ownSupport?.referenceCoverage ?? null, scoredNights: report.summary[metric].scoredNights, coverageReducedNights: report.summary[metric].coverageReducedNights } : null const key = (value: number | null | undefined) => value ?? Number.POSITIVE_INFINITY @@ -72,25 +103,34 @@ export function rankCandidates(reports: LocalFitReport[]) { breathingRate: { tuning: view(tuning, 'breathing_rate'), validation: view(validation, 'breathing_rate') }, hrv: { tuning: view(tuning, 'window_ibi_variability_v1'), validation: view(validation, 'window_ibi_variability_v1') } } }) - rows.sort((a, b) => key(a.heartRate.tuning?.maeDifference) - key(b.heartRate.tuning?.maeDifference) - || key(a.heartRate.validation?.maeDifference) - key(b.heartRate.validation?.maeDifference) + // A candidate below the coverage floor ranks after every one above it, so + // abstaining on most of the night cannot win on error alone. + const kept = (row: typeof rows[number]) => (row.heartRate.tuning?.coverage ?? 1) >= minimumCoverage ? 0 : 1 + rows.sort((a, b) => kept(a) - kept(b) + || key(a.heartRate.tuning?.ownMae) - key(b.heartRate.tuning?.ownMae) + || key(a.heartRate.validation?.ownMae) - key(b.heartRate.validation?.ownMae) || (a.heartRate.tuning?.coverageReducedNights ?? 0) - (b.heartRate.tuning?.coverageReducedNights ?? 0) || a.changedDefaults - b.changedDefaults || a.id.localeCompare(b.id)) return { version: 'biometrics-local-fit-ranking-v1' as const, label: 'exploratory-local' as const, evidence: 'not_activation_evidence' as const, - rankedBy: 'tuning.heart_rate.equalNightMeanMaeDifference_vs_defaults_common_support' as const, + rankedBy: 'tuning.heart_rate.ownSupport_equalNightMeanMae_with_reference_coverage_floor' as const, minimumCoverage, rows: rows.map((row, index) => ({ rank: index + 1, ...row })) } } const format = (value: number | null | undefined, digits = 3) => value === null || value === undefined ? 'n/a' : value.toFixed(digits) +const percent = (value: number | null | undefined) => value === null || value === undefined ? 'n/a' : `${Math.round(value * 100)}%` export function rankingMarkdown(ranking: ReturnType) { const holdout = ranking.rows.some(row => row.heartRate.holdout) - const lines = ['_Exploratory, computed off-pod. Not activation evidence._', '', - `| # | candidate | tuning HR MAE | tuning Δ vs defaults | validation HR MAE | validation Δ |${holdout ? ' holdout HR MAE | holdout Δ |' : ''} coverage-reduced nights (t/v) | BR Δ (t) | HRV Δ (t) |`, - `| ---: | --- | ---: | ---: | ---: | ---: |${holdout ? ' ---: | ---: |' : ''} :---: | ---: | ---: |`] + const splits = ['tuning', 'validation', ...(holdout ? ['holdout'] : [])] as const + const lines = [`_Exploratory, computed off-pod. Not activation evidence. Ranked by tuning HR error on each candidate's own readings, among candidates keeping ≥${percent(ranking.minimumCoverage)} of the defaults' Watch coverage._`, '', + `| # | candidate | ${splits.map(split => `${split} HR MAE | ${split} Δ common | ${split} coverage`).join(' | ')} | BR Δ (t) | HRV Δ (t) |`, + `| ---: | --- | ${splits.map(() => '---: | ---: | ---:').join(' | ')} | ---: | ---: |`] for (const row of ranking.rows) { - const held = holdout ? ` ${format(row.heartRate.holdout?.mae)} | ${format(row.heartRate.holdout?.maeDifference)} |` : '' - lines.push(`| ${row.rank} | ${row.id} | ${format(row.heartRate.tuning?.mae)} | ${format(row.heartRate.tuning?.maeDifference)} | ${format(row.heartRate.validation?.mae)} | ${format(row.heartRate.validation?.maeDifference)} |${held} ${row.heartRate.tuning?.coverageReducedNights ?? 'n/a'}/${row.heartRate.validation?.coverageReducedNights ?? 'n/a'} | ${format(row.breathingRate.tuning?.maeDifference)} | ${format(row.hrv.tuning?.maeDifference)} |`) + const cells = splits.map((split) => { + const view = row.heartRate[split as 'tuning'] + return `${format(view?.ownMae)} | ${format(view?.maeDifference)} | ${percent(view?.coverage)}` + }) + lines.push(`| ${row.rank} | ${row.id} | ${cells.join(' | ')} | ${format(row.breathingRate.tuning?.maeDifference)} | ${format(row.hrv.tuning?.maeDifference)} |`) } return `${lines.join('\n')}\n` } diff --git a/src/biometrics/algorithms/registry-core.ts b/src/biometrics/algorithms/registry-core.ts index 2f62a2f6..33f31847 100644 --- a/src/biometrics/algorithms/registry-core.ts +++ b/src/biometrics/algorithms/registry-core.ts @@ -16,7 +16,7 @@ interface ParameterSchema { } export interface BiometricConfig { - heart_rate: { trackingToleranceBpm: number, trackingHistoryLength: number } + heart_rate: { trackingToleranceBpm: number, trackingHistoryLength: number, minimumPeakScore: number } breathing_rate: { intervalLowerRatio: number, intervalUpperRatio: number } hrv_index: { harmonicTolerance: number, minimumWindows: number } sleep_stages: StageClassifier diff --git a/src/components/biometrics/Calibration/CalibrationProfileCard.tsx b/src/components/biometrics/Calibration/CalibrationProfileCard.tsx index e4b46911..0b9cc585 100644 --- a/src/components/biometrics/Calibration/CalibrationProfileCard.tsx +++ b/src/components/biometrics/Calibration/CalibrationProfileCard.tsx @@ -151,7 +151,7 @@ export function ProfileCard({ saved, previous, previousIsDefaults, draft, onDraf set(path, v)} /> ) - const tol = 'heart_rate.trackingToleranceBpm', hist = 'heart_rate.trackingHistoryLength' + const tol = 'heart_rate.trackingToleranceBpm', hist = 'heart_rate.trackingHistoryLength', peak = 'heart_rate.minimumPeakScore' const low = 'breathing_rate.intervalLowerRatio', high = 'breathing_rate.intervalUpperRatio', windows = 'hrv_index.minimumWindows' return ( @@ -192,6 +192,14 @@ export function ProfileCard({ saved, previous, previousIsDefaults, draft, onDraf , sub(hist), slider(hist, t`Tracking history length`), effect([hist], 'heart_rate', 'bpm'), true, )} + {row( + <> + {t`Report no heart rate when the beat signal scores below`} + {' '} + {value(peak)} + , + sub(peak), slider(peak, t`Minimum beat signal score`), effect([peak], 'heart_rate', 'bpm'), true, + )} {t`BREATHING`} {row( diff --git a/src/components/biometrics/Calibration/calibration-model.ts b/src/components/biometrics/Calibration/calibration-model.ts index b40cfbbd..4749eb38 100644 --- a/src/components/biometrics/Calibration/calibration-model.ts +++ b/src/components/biometrics/Calibration/calibration-model.ts @@ -11,6 +11,7 @@ import type { NumericReportView } from './report-view' export type ParamPath = | 'heart_rate.trackingToleranceBpm' | 'heart_rate.trackingHistoryLength' + | 'heart_rate.minimumPeakScore' | 'breathing_rate.intervalLowerRatio' | 'breathing_rate.intervalUpperRatio' | 'hrv_index.minimumWindows' @@ -21,6 +22,7 @@ export interface ParamSpec { path: ParamPath, key: string, min: number, max: num export const PARAMS: Record = { 'heart_rate.trackingToleranceBpm': { path: 'heart_rate.trackingToleranceBpm', key: 'trackingToleranceBpm', min: 5, max: 30, step: 1, decimals: 0 }, 'heart_rate.trackingHistoryLength': { path: 'heart_rate.trackingHistoryLength', key: 'trackingHistoryLength', min: 1, max: 10, step: 1, decimals: 0 }, + 'heart_rate.minimumPeakScore': { path: 'heart_rate.minimumPeakScore', key: 'minimumPeakScore', min: 0.1, max: 0.8, step: 0.05, decimals: 2 }, 'breathing_rate.intervalLowerRatio': { path: 'breathing_rate.intervalLowerRatio', key: 'intervalLowerRatio', min: 0.25, max: 0.9, step: 0.05, decimals: 2 }, 'breathing_rate.intervalUpperRatio': { path: 'breathing_rate.intervalUpperRatio', key: 'intervalUpperRatio', min: 1.1, max: 2, step: 0.05, decimals: 2 }, 'hrv_index.minimumWindows': { path: 'hrv_index.minimumWindows', key: 'minimumWindows', min: 10, max: 30, step: 1, decimals: 0 }, diff --git a/src/components/biometrics/Calibration/tests/BiometricCalibration.test.tsx b/src/components/biometrics/Calibration/tests/BiometricCalibration.test.tsx index 990db9fc..3ed1d135 100644 --- a/src/components/biometrics/Calibration/tests/BiometricCalibration.test.tsx +++ b/src/components/biometrics/Calibration/tests/BiometricCalibration.test.tsx @@ -140,7 +140,7 @@ it('reads the backtest per night and per split, with holdout the one to trust', expect(screen.getByText('frozen Oct 12 · 2.1 MB')).toBeTruthy() expect(screen.getAllByText('watch').length).toBe(3) expect(screen.getByText('holdout −0.8 bpm · covered 90%')).toBeTruthy() - expect(screen.getAllByText('unchanged').length).toBe(3) + expect(screen.getAllByText('unchanged').length).toBe(4) const nights = screen.getByRole('listbox', { name: 'Dataset nights' }) fireEvent.click(within(nights).getByRole('option', { name: /Sat 10/ })) expect(within(nights).getByRole('option', { name: /Sat 10/ }).getAttribute('aria-selected')).toBe('true') diff --git a/src/demo/biometrics/worker-build.json b/src/demo/biometrics/worker-build.json index 947379f7..b08afa27 100644 --- a/src/demo/biometrics/worker-build.json +++ b/src/demo/biometrics/worker-build.json @@ -1,5 +1,5 @@ { - "buildHash": "bdff1b5d6f715954aaae41d70fd499346288585a2ca80cf0dba9665f560a6ed4", + "buildHash": "5887f41b976bf0eea8192592c72b308eb153957564f0730cc324c29345e96ad2", "identity": "esbuild-browser-evaluator-bundle", "simulated": true } diff --git a/src/lib/i18n/locales/en.po b/src/lib/i18n/locales/en.po index 3158a94e..2343f402 100644 --- a/src/lib/i18n/locales/en.po +++ b/src/lib/i18n/locales/en.po @@ -13,15 +13,19 @@ msgstr "" "Language-Team: \n" "Plural-Forms: \n" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:169 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:250 msgid ", with {count} overrides" msgstr ", with {count} overrides" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:169 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:250 msgid ", with 1 override" msgstr ", with 1 override" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:369 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:250 +msgid ", with no overrides" +msgstr ", with no overrides" + +#: src/components/biometrics/Calibration/CalibrationPage.tsx:393 msgid ". Separate from the profile." msgstr ". Separate from the profile." @@ -36,7 +40,7 @@ msgstr "{0} · {1} · revision {2}" #. placeholder {1}: label.day #. placeholder {2}: fmt1(prev) #. placeholder {3}: fmt1(saved) -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:198 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:228 msgid "{0} {1}: previous {2}, saved {3} bpm" msgstr "{0} {1}: previous {2}, saved {3} bpm" @@ -44,12 +48,12 @@ msgstr "{0} {1}: previous {2}, saved {3} bpm" #. placeholder {1}: avg(n => n.previous.heart_rate.mae as number) #. placeholder {2}: avg(n => n.saved.heart_rate.mae as number) #. placeholder {3}: shortDuration(job.updatedAtMs - job.createdAtMs) -#: src/components/biometrics/Calibration/CalibrationPage.tsx:216 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:231 msgid "{0} HR error {1} → {2} bpm · {3}" msgstr "{0} HR error {1} → {2} bpm · {3}" #. placeholder {0}: i.summary.accepted -#: src/components/biometrics/Calibration/CalibrationPage.tsx:227 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:242 msgid "{0} records" msgstr "{0} records" @@ -69,15 +73,15 @@ msgstr "A saved request does not mean execution has started or succeeded. Inspec msgid "A saved revision is not evidence of validation or processor adoption." msgstr "A saved revision is not evidence of validation or processor adoption." -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:109 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:177 msgid "Accept an estimate when it is within" msgstr "Accept an estimate when it is within" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:156 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:237 msgid "accepted 10-second IBI windows" msgstr "accepted 10-second IBI windows" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:123 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:191 msgid "accepted estimates in the tracker" msgstr "accepted estimates in the tracker" @@ -216,7 +220,7 @@ msgid "Available reference records by metric (across revisions)" msgstr "Available reference records by metric (across revisions)" #. placeholder {0}: group.latest.id.slice(0, 4) -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:181 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:197 msgid "Backtest {0}. Frozen dataset replayed through the previous and saved profiles; nothing here changes the pod." msgstr "Backtest {0}. Frozen dataset replayed through the previous and saved profiles; nothing here changes the pod." @@ -225,7 +229,7 @@ msgid "Backtest jobs" msgstr "Backtest jobs" #. placeholder {0}: splitLabel[job.split] -#: src/components/biometrics/Calibration/CalibrationPage.tsx:220 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:235 msgid "Backtest on {0} nights failed" msgstr "Backtest on {0} nights failed" @@ -258,19 +262,19 @@ msgstr "Before configuration hash" msgid "Biometric calibration" msgstr "Biometric calibration" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:264 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:279 msgid "Biometric calibration side" msgstr "Biometric calibration side" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:113 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:181 msgid "bpm of the tracker" msgstr "bpm of the tracker" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:128 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:204 msgid "BREATHING" msgstr "BREATHING" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:173 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:189 msgid "Breathing error" msgstr "Breathing error" @@ -394,11 +398,11 @@ msgstr "Candidate sleep-stage rules" msgid "Candidate spike threshold" msgstr "Candidate spike threshold" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:94 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:162 msgid "Changed values are white; the previous profile (System defaults) sits underneath. Moving a control edits the saved profile; saving again re-requests it. The processor adopts a profile only after it acknowledges a request." msgstr "Changed values are white; the previous profile (System defaults) sits underneath. Moving a control edits the saved profile; saving again re-requests it. The processor adopts a profile only after it acknowledges a request." -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:95 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:163 msgid "Changed values are white; the previous profile sits underneath. Moving a control edits the saved profile; saving again re-requests it. The processor adopts a profile only after it acknowledges a request." msgstr "Changed values are white; the previous profile sits underneath. Moving a control edits the saved profile; saving again re-requests it. The processor adopts a profile only after it acknowledges a request." @@ -426,6 +430,10 @@ msgstr "Choose a template" msgid "Choose the protocol before looking at results. Creating an experiment does not run a job or apply a profile." msgstr "Choose the protocol before looking at results. Creating an experiment does not run a job or apply a profile." +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:78 +msgid "Choosing nights the reference covers…" +msgstr "Choosing nights the reference covers…" + #: src/components/biometrics/Calibration/ReferenceOverlay.tsx:38 msgid "Circles are recomputed readings; squares are reference points; horizontal bars are reference intervals. Only the two displayed pages are shown. This visual overlay does not create new matched pairs or scores." msgstr "Circles are recomputed readings; squares are reference points; horizontal bars are reference intervals. Only the two displayed pages are shown. This visual overlay does not create new matched pairs or scores." @@ -434,7 +442,7 @@ msgstr "Circles are recomputed readings; squares are reference points; horizonta msgid "Clear this night’s selection" msgstr "Clear this night’s selection" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:187 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:205 msgid "click a night" msgstr "click a night" @@ -544,7 +552,7 @@ msgstr "Completed nights" msgid "Compressed bytes: {0}" msgstr "Compressed bytes: {0}" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:372 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:396 msgid "config id" msgstr "config id" @@ -586,11 +594,11 @@ msgstr "Coverage reduced" msgid "Coverage reduced in at least one evaluated window" msgstr "Coverage reduced in at least one evaluated window" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:61 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:129 msgid "covered" msgstr "covered" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:361 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:380 msgid "current" msgstr "current" @@ -626,7 +634,7 @@ msgstr "Dataset" msgid "Dataset hash" msgstr "Dataset hash" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:84 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:99 msgid "Dataset nights" msgstr "Dataset nights" @@ -661,7 +669,7 @@ msgid "Descriptive evaluation only" msgstr "Descriptive evaluation only" #. placeholder {0}: shortDuration(run.durationMs) -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:150 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:170 msgid "done · {0}" msgstr "done · {0}" @@ -677,11 +685,15 @@ msgstr "Durations cover sequential Python work through result generation. Memory msgid "Each configuration on its own available intervals" msgstr "Each configuration on its own available intervals" +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:268 +msgid "Each night's heart rate against the reference appears here once a backtest has run." +msgstr "Each night's heart rate against the reference appears here once a backtest has run." + #: src/components/biometrics/Calibration/ProfileLibrary.tsx:34 msgid "Edit a new candidate from this revision" msgstr "Edit a new candidate from this revision" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:192 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:273 msgid "Edit overrides" msgstr "Edit overrides" @@ -744,7 +756,7 @@ msgstr "Estimate exploratory whole-night uncertainty" msgid "Evaluated minutes: {0}" msgstr "Evaluated minutes: {0}" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:75 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:90 msgid "Evaluation dataset" msgstr "Evaluation dataset" @@ -812,7 +824,7 @@ msgstr "Execution state is separate from the strength of biometric evidence." msgid "Experiment saved: {0}" msgstr "Experiment saved: {0}" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:361 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:380 msgid "expired" msgstr "expired" @@ -841,7 +853,7 @@ msgstr "Exploratory interval estimated" msgid "F1" msgstr "F1" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:150 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:170 msgid "failed" msgstr "failed" @@ -851,7 +863,7 @@ msgstr "failed" msgid "Failed" msgstr "Failed" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:184 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:265 msgid "Fallback" msgstr "Fallback" @@ -860,16 +872,16 @@ msgstr "Fallback" msgid "Fallback stage" msgstr "Fallback stage" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:202 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:217 msgid "Fell back to a compatible profile" msgstr "Fell back to a compatible profile" #. placeholder {0}: splitLabel[job.split] -#: src/components/biometrics/Calibration/CalibrationPage.tsx:220 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:235 msgid "Finished a backtest on {0} nights" msgstr "Finished a backtest on {0} nights" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:314 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:344 msgid "first 500 readings" msgstr "first 500 readings" @@ -897,7 +909,7 @@ msgstr "Fixed offset added to pod timestamps (ms, ±3600000)" msgid "Flags" msgstr "Flags" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:128 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:143 msgid "Freeze a new dataset" msgstr "Freeze a new dataset" @@ -909,29 +921,41 @@ msgstr "Freeze evaluation inputs" msgid "Freeze selected inputs" msgstr "Freeze selected inputs" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:361 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:85 +msgid "Freezing failed." +msgstr "Freezing failed." + +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:79 +msgid "Freezing the nights…" +msgstr "Freezing the nights…" + +#: src/components/biometrics/Calibration/CalibrationPage.tsx:380 msgid "freshness unknown" msgstr "freshness unknown" #. placeholder {0}: d.manifest.splitProtocol.assignments.length -#: src/components/biometrics/Calibration/CalibrationPage.tsx:224 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:239 msgid "Froze a dataset of {0} nights" msgstr "Froze a dataset of {0} nights" #. placeholder {0}: formatDay(dataset.createdAtMs, timezone) #. placeholder {1}: formatBytes(dataset.byteLength) -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:79 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:94 msgid "frozen {0} · {1}" msgstr "frozen {0} · {1}" +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:84 +msgid "Frozen and queued. The backtest replays every split; results appear as each finishes." +msgstr "Frozen and queued. The backtest replays every split; results appear as each finishes." + #. placeholder {0}: nights.length #. placeholder {1}: sources.join(' + ') -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:69 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:71 msgid "Frozen copy of {0} nights and {1} reference. Later edits or deletions do not touch it. Whole nights are split before scoring; a split is not evidence on its own." msgstr "Frozen copy of {0} nights and {1} reference. Later edits or deletions do not touch it. Whole nights are split before scoring; a split is not evidence on its own." #. placeholder {0}: nights.length -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:70 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:72 msgid "Frozen copy of {0} nights. Later edits or deletions do not touch it. Whole nights are split before scoring; a split is not evidence on its own." msgstr "Frozen copy of {0} nights. Later edits or deletions do not touch it. Whole nights are split before scoring; a split is not evidence on its own." @@ -971,7 +995,7 @@ msgstr "Frozen stage objective thresholds" msgid "Groups use native reference timestamps and record types, independently of candidate output. Half-window groups exclude intervals crossing the midpoint. Groups overlap and are not independent experiments. These are not motion or artifact subgroups." msgstr "Groups use native reference timestamps and record types, independently of candidate output. Half-window groups exclude intervals crossing the midpoint. Groups overlap and are not independent experiments. These are not motion or artifact subgroups." -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:228 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:258 msgid "H holdout" msgstr "H holdout" @@ -983,13 +1007,13 @@ msgstr "Health was sampled at admission checks, normally five seconds apart. Uno msgid "Heart rate" msgstr "Heart rate" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:106 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:174 msgid "HEART RATE" msgstr "HEART RATE" #. placeholder {0}: label.weekday #. placeholder {1}: label.day -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:291 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:321 msgid "Heart rate · {0} {1}" msgstr "Heart rate · {0} {1}" @@ -1003,7 +1027,7 @@ msgstr "Heart rate (bpm)" msgid "Heart-rate spike threshold (standard deviations)" msgstr "Heart-rate spike threshold (standard deviations)" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:295 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:325 msgid "Heart-rate trace" msgstr "Heart-rate trace" @@ -1011,7 +1035,7 @@ msgstr "Heart-rate trace" msgid "Hello, tRPC test page!" msgstr "Hello, tRPC test page!" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:377 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:401 msgid "hide" msgstr "hide" @@ -1022,8 +1046,8 @@ msgstr "hide" msgid "Higher agreement: {0}; lower: {1}; unchanged: {2}" msgstr "Higher agreement: {0}; lower: {1}; unchanged: {2}" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:120 -#: src/components/biometrics/Calibration/CalibrationPage.tsx:206 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:135 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:221 msgid "holdout" msgstr "holdout" @@ -1040,23 +1064,23 @@ msgstr "Holdout" msgid "Holdout accesses recorded for this experiment: {0}" msgstr "Holdout accesses recorded for this experiment: {0}" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:258 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:288 msgid "Holdout is the number to trust; tuning nights were used to pick these values." msgstr "Holdout is the number to trust; tuning nights were used to pick these values." -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:170 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:186 msgid "HR error · holdout" msgstr "HR error · holdout" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:168 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:184 msgid "HR error · tuning" msgstr "HR error · tuning" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:169 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:185 msgid "HR error · validation" msgstr "HR error · validation" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:186 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:204 msgid "HR error per night · bpm vs reference" msgstr "HR error per night · bpm vs reference" @@ -1068,11 +1092,11 @@ msgstr "HRV definition" msgid "HRV definitions differ or are partly unknown. A combined HRV average is unavailable." msgstr "HRV definitions differ or are partly unknown. A combined HRV average is unavailable." -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:149 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:230 msgid "HRV INDEX" msgstr "HRV INDEX" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:172 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:188 msgid "HRV index error" msgstr "HRV index error" @@ -1109,7 +1133,7 @@ msgstr "Import accepted records and omit rejected records" msgid "Import preview" msgstr "Import preview" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:126 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:141 msgid "Import reference" msgstr "Import reference" @@ -1145,7 +1169,7 @@ msgid "Inherited default" msgstr "Inherited default" #. placeholder {0}: stages.ruleCount -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:166 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:247 msgid "Inherits the {0} rules and fallback from" msgstr "Inherits the {0} rules and fallback from" @@ -1240,11 +1264,11 @@ msgstr "Keep report" msgid "Keep retained inputs" msgstr "Keep retained inputs" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:318 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:337 msgid "Keep saved profile" msgstr "Keep saved profile" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:119 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:187 msgid "Keep the last" msgstr "Keep the last" @@ -1280,7 +1304,11 @@ msgstr "Light" msgid "Limitations" msgstr "Limitations" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:144 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:77 +msgid "Linking Watch data already on the pod…" +msgstr "Linking Watch data already on the pod…" + +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:223 msgid "Longest breathing interval (× median)" msgstr "Longest breathing interval (× median)" @@ -1333,7 +1361,7 @@ msgstr "Mean absolute error on shared timestamps: active {0}, candidate {1}." msgid "Measured Python processes: {0} of {1}" msgstr "Measured Python processes: {0} of {1}" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:366 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:390 msgid "Measured sensor calibration for this side:" msgstr "Measured sensor calibration for this side:" @@ -1365,11 +1393,15 @@ msgstr "Metric window: {0} seconds." msgid "Metrics: {0}" msgstr "Metrics: {0}" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:202 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:217 msgid "Migrated sleep-stage settings into the profile" msgstr "Migrated sleep-stage settings into the profile" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:159 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:201 +msgid "Minimum beat signal score" +msgstr "Minimum beat signal score" + +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:240 msgid "Minimum IBI windows" msgstr "Minimum IBI windows" @@ -1469,7 +1501,7 @@ msgstr "Next trace page" msgid "Night {0}" msgstr "Night {0}" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:171 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:187 msgid "Night covered" msgstr "Night covered" @@ -1482,13 +1514,29 @@ msgid "No accepted time range." msgstr "No accepted time range." #: src/components/biometrics/Calibration/CalibrationEvidence.tsx:158 -msgid "No backtest has replayed the saved profile yet. Saving a change re-runs the latest experiment on its tuning and validation nights; nothing here changes the pod." -msgstr "No backtest has replayed the saved profile yet. Saving a change re-runs the latest experiment on its tuning and validation nights; nothing here changes the pod." +#~ msgid "No backtest has replayed the saved profile yet. Saving a change re-runs the latest experiment on its tuning and validation nights; nothing here changes the pod." +#~ msgstr "No backtest has replayed the saved profile yet. Saving a change re-runs the latest experiment on its tuning and validation nights; nothing here changes the pod." + +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:177 +msgid "No backtest has replayed the saved profile yet. Saving a change replays the frozen dataset on its tuning, validation and holdout nights; nothing here changes the pod." +msgstr "No backtest has replayed the saved profile yet. Saving a change replays the frozen dataset on its tuning, validation and holdout nights; nothing here changes the pod." #: src/components/biometrics/Calibration/BacktestJobs.tsx:28 msgid "No backtest jobs for this side." msgstr "No backtest jobs for this side." +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:175 +msgid "No backtest yet. Freeze a new dataset below; it replays the newest referenced nights through the previous and saved profiles. Nothing here changes the pod." +msgstr "No backtest yet. Freeze a new dataset below; it replays the newest referenced nights through the previous and saved profiles. Nothing here changes the pod." + +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:174 +msgid "No backtest yet. Import a reference for this side, then freeze nights that have it; the replay scores the previous and saved profiles against it. Nothing here changes the pod." +msgstr "No backtest yet. Import a reference for this side, then freeze nights that have it; the replay scores the previous and saved profiles against it. Nothing here changes the pod." + +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:176 +msgid "No backtest yet. This dataset has no waveform replay; freeze a new dataset below to set one up. Nothing here changes the pod." +msgstr "No backtest yet. This dataset has no waveform replay; freeze a new dataset below to set one up. Nothing here changes the pod." + #: src/components/biometrics/Calibration/BacktestReport.tsx:38 msgid "No committed report is available yet." msgstr "No committed report is available yet." @@ -1506,8 +1554,12 @@ msgid "No coverage reduction detected" msgstr "No coverage reduction detected" #: src/components/biometrics/Calibration/CalibrationEvidence.tsx:71 -msgid "No dataset is frozen for this side yet. Freeze recorded nights with an imported reference to backtest a profile." -msgstr "No dataset is frozen for this side yet. Freeze recorded nights with an imported reference to backtest a profile." +#~ msgid "No dataset is frozen for this side yet. Freeze recorded nights with an imported reference to backtest a profile." +#~ msgstr "No dataset is frozen for this side yet. Freeze recorded nights with an imported reference to backtest a profile." + +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:73 +msgid "No dataset is frozen for this side yet. Freezing takes the newest nights a reference covers, with their RAW archives, so a backtest can replay them." +msgstr "No dataset is frozen for this side yet. Freezing takes the newest nights a reference covers, with their RAW archives, so a backtest can replay them." #: src/components/biometrics/Calibration/BacktestReport.tsx:62 #: src/components/biometrics/Calibration/NumericReport.tsx:28 @@ -1563,7 +1615,7 @@ msgstr "No prospective runs for this side." msgid "No recorded nights for this side." msgstr "No recorded nights for this side." -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:101 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:116 msgid "no reference" msgstr "no reference" @@ -1571,7 +1623,7 @@ msgstr "no reference" msgid "No reference" msgstr "No reference" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:286 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:316 msgid "no reference device · excluded from error" msgstr "no reference device · excluded from error" @@ -1635,7 +1687,7 @@ msgstr "None" msgid "Normalized reference measurements" msgstr "Normalized reference measurements" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:362 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:386 msgid "not reported" msgstr "not reported" @@ -1648,6 +1700,10 @@ msgstr "Not reported" msgid "Not requested" msgstr "Not requested" +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:200 +msgid "not run" +msgstr "not run" + #: src/components/biometrics/Calibration/BacktestReport.tsx:63 #: src/components/biometrics/Calibration/NumericReport.tsx:24 #: src/components/biometrics/Calibration/NumericReport.tsx:25 @@ -1656,12 +1712,12 @@ msgstr "Not requested" msgid "Not scored" msgstr "Not scored" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:55 -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:57 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:123 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:125 msgid "not tested" msgstr "not tested" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:330 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:349 msgid "Nothing changes until you move a control or restore." msgstr "Nothing changes until you move a control or restore." @@ -1770,7 +1826,7 @@ msgstr "Older profiles" msgid "Only the current page is plotted. Dots represent individual readings; gaps and missing values are not interpolated. Original stored readings remain unchanged." msgstr "Only the current page is plotted. Dots represent individual readings; gaps and missing values are not interpolated. Original stored readings remain unchanged." -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:135 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:211 msgid "or longer than" msgstr "or longer than" @@ -1821,7 +1877,7 @@ msgstr "Override intent can change even when the resolved value stays the same." msgid "Overview" msgstr "Overview" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:330 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:349 msgid "Overwrites the saved profile and requests it; the current one becomes previous." msgstr "Overwrites the saved profile and requests it; the current one becomes previous." @@ -1870,7 +1926,7 @@ msgstr "Permanently release inputs" msgid "Permanently release report" msgstr "Permanently release report" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:238 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:268 msgid "Pick a scored night to see its heart-rate trace." msgstr "Pick a scored night to see its heart-rate trace." @@ -1957,11 +2013,11 @@ msgstr "Preview import" msgid "Preview normalized records before saving them on the pod." msgstr "Preview normalized records before saving them on the pod." -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:222 -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:245 -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:312 -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:65 -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:141 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:252 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:275 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:342 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:133 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:217 msgid "previous" msgstr "previous" @@ -1999,12 +2055,12 @@ msgid "Processor" msgstr "Processor" #. placeholder {0}: event.generation -#: src/components/biometrics/Calibration/CalibrationPage.tsx:203 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:218 msgid "Processor acknowledged generation {0}" msgstr "Processor acknowledged generation {0}" #. placeholder {0}: event.generation -#: src/components/biometrics/Calibration/CalibrationPage.tsx:204 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:219 msgid "Processor rejected generation {0}" msgstr "Processor rejected generation {0}" @@ -2042,7 +2098,7 @@ msgstr "Prospective observations" msgid "Protocol hash" msgstr "Protocol hash" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:287 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:317 msgid "quality" msgstr "quality" @@ -2054,7 +2110,7 @@ msgstr "Quality" msgid "Queue backtest" msgstr "Queue backtest" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:150 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:170 msgid "queued" msgstr "queued" @@ -2062,10 +2118,20 @@ msgstr "queued" msgid "Queued" msgstr "Queued" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:65 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:83 +msgid "Queueing the backtest…" +msgstr "Queueing the backtest…" + +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:133 msgid "range" msgstr "range" +#. placeholder {0}: setup.done ?? 0 +#. placeholder {1}: setup.total ?? 0 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:80 +msgid "Reading RAW archives · {0} / {1}" +msgstr "Reading RAW archives · {0} / {1}" + #: src/components/biometrics/Calibration/StageReport.tsx:52 msgid "Recall" msgstr "Recall" @@ -2104,8 +2170,8 @@ msgstr "Recorded configurations: {0}; readings with unknown configuration: {1}." msgid "Recorded nights" msgstr "Recorded nights" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:313 -#: src/components/biometrics/Calibration/CalibrationPage.tsx:128 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:343 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:143 msgid "reference" msgstr "reference" @@ -2241,7 +2307,7 @@ msgstr "Refresh report" msgid "Regression under the evaluation policy" msgstr "Regression under the evaluation policy" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:131 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:207 msgid "Reject intervals shorter than" msgstr "Reject intervals shorter than" @@ -2261,7 +2327,7 @@ msgstr "Release retained inputs" msgid "Release these retained inputs permanently? Existing reports and audit metadata remain. Active jobs must finish or be cancelled first." msgstr "Release these retained inputs permanently? Existing reports and audit metadata remain. Active jobs must finish or be cancelled first." -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:79 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:94 msgid "released" msgstr "released" @@ -2322,7 +2388,11 @@ msgstr "Report hash" msgid "Report identity" msgstr "Report identity" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:152 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:197 +msgid "Report no heart rate when the beat signal scores below" +msgstr "Report no heart rate when the beat signal scores below" + +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:233 msgid "Report the index only after" msgstr "Report the index only after" @@ -2339,7 +2409,7 @@ msgid "Requested" msgstr "Requested" #. placeholder {0}: event.generation -#: src/components/biometrics/Calibration/CalibrationPage.tsx:198 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:213 msgid "Requested activation · generation {0}" msgstr "Requested activation · generation {0}" @@ -2375,7 +2445,7 @@ msgstr "Restart archive scan" msgid "Restore and request" msgstr "Restore and request" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:326 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:345 msgid "Restore previous" msgstr "Restore previous" @@ -2398,11 +2468,11 @@ msgstr "Restore the previous profile" msgid "Restore… ▾" msgstr "Restore… ▾" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:201 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:216 msgid "Restored system defaults and requested them" msgstr "Restored system defaults and requested them" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:200 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:215 msgid "Restored the previous profile and requested it" msgstr "Restored the previous profile and requested it" @@ -2435,6 +2505,10 @@ msgstr "Retained input integrity checked by the server." msgid "Retained waveform replay" msgstr "Retained waveform replay" +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:81 +msgid "Retaining the archives…" +msgstr "Retaining the archives…" + #: src/components/biometrics/Calibration/ManualProfileActivation.tsx:62 #: src/components/biometrics/Calibration/ValidatedProfileActivation.tsx:46 msgid "Retry exact activation request" @@ -2489,11 +2563,11 @@ msgid "Row {0}: {1}" msgstr "Row {0}: {1}" #. placeholder {0}: o.rule -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:174 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:255 msgid "Rule {0}" msgstr "Rule {0}" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:150 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:170 msgid "running" msgstr "running" @@ -2510,7 +2584,7 @@ msgstr "Sampled worker memory peak: {0} MiB. Short peaks between samples may be msgid "Save a candidate for evaluation. This does not change live processing." msgstr "Save a candidate for evaluation. This does not change live processing." -#: src/components/biometrics/Calibration/CalibrationPage.tsx:318 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:337 msgid "Save and request" msgstr "Save and request" @@ -2522,7 +2596,7 @@ msgstr "Save candidate without applying" msgid "Save evaluation protocol" msgstr "Save evaluation protocol" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:174 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:189 msgid "Save failed." msgstr "Save failed." @@ -2530,9 +2604,9 @@ msgstr "Save failed." msgid "Save previewed import" msgstr "Save previewed import" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:226 -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:246 -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:311 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:256 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:276 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:341 msgid "saved" msgstr "saved" @@ -2553,7 +2627,7 @@ msgstr "Saved experiments" msgid "Saved observations: {0}" msgstr "Saved observations: {0}" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:92 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:160 msgid "Saved profile against previous" msgstr "Saved profile against previous" @@ -2567,7 +2641,7 @@ msgstr "Saved profile revisions" msgid "Saved sample times: {0}. Planned observation intervals: {1}." msgstr "Saved sample times: {0}. Planned observation intervals: {1}." -#: src/components/biometrics/Calibration/CalibrationPage.tsx:196 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:211 msgid "Saved the profile" msgstr "Saved the profile" @@ -2575,7 +2649,7 @@ msgstr "Saved the profile" msgid "Saves it as the profile and requests it; the current saved profile becomes previous. The processor keeps running what it has until it acknowledges the request." msgstr "Saves it as the profile and requests it; the current saved profile becomes previous. The processor keeps running what it has until it acknowledges the request." -#: src/components/biometrics/Calibration/CalibrationPage.tsx:318 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:337 msgid "Saving…" msgstr "Saving…" @@ -2654,6 +2728,10 @@ msgstr "Selected sessions: {0} / 60" msgid "Sessions: {0}" msgstr "Sessions: {0}" +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:82 +msgid "Setting up the replay…" +msgstr "Setting up the replay…" + #: src/components/biometrics/Calibration/NumericReport.tsx:108 msgid "Shared MAE difference" msgstr "Shared MAE difference" @@ -2676,11 +2754,11 @@ msgstr "Shared-timestamp comparisons are unavailable in this report." msgid "Shared-timestamp comparisons. Differences are comparison minus baseline; negative error differences mean lower error on shared observations." msgstr "Shared-timestamp comparisons. Differences are comparison minus baseline; negative error differences mean lower error on shared observations." -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:143 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:222 msgid "Shortest breathing interval (× median)" msgstr "Shortest breathing interval (× median)" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:377 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:401 msgid "show" msgstr "show" @@ -2705,11 +2783,11 @@ msgstr "Signed bias" msgid "Simulated demo: generated readings, no hardware execution or qualification." msgstr "Simulated demo: generated readings, no hardware execution or qualification." -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:168 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:249 msgid "Sleep stages" msgstr "Sleep stages" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:162 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:243 msgid "SLEEP STAGES" msgstr "SLEEP STAGES" @@ -2826,7 +2904,7 @@ msgid "Stop recorded" msgstr "Stop recorded" #. placeholder {0}: i.summary.source -#: src/components/biometrics/Calibration/CalibrationPage.tsx:227 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:242 msgid "Stored a {0} reference import" msgstr "Stored a {0} reference import" @@ -2848,7 +2926,7 @@ msgstr "Stored-reading comparison available" #: src/components/biometrics/Calibration/BacktestJobs.tsx:64 #: src/components/biometrics/Calibration/BacktestReport.tsx:60 #: src/components/biometrics/Calibration/CalibrationControls.tsx:80 -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:103 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:171 #: src/components/biometrics/Calibration/NumericReport.tsx:26 #: src/components/biometrics/Calibration/NumericTrace.tsx:147 #: src/components/biometrics/Calibration/ProfileComparison.tsx:29 @@ -2858,7 +2936,7 @@ msgstr "Stored-reading comparison available" msgid "System defaults" msgstr "System defaults" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:54 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:122 msgid "tested on save" msgstr "tested on save" @@ -2879,7 +2957,7 @@ msgstr "The frozen offset of {clockOffsetMs} ms is added to recomputed timestamp msgid "The last archive was unavailable. Continue to inspect the next archive." msgstr "The last archive was unavailable. Continue to inspect the next archive." -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:139 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:215 msgid "the median" msgstr "the median" @@ -2912,7 +2990,7 @@ msgstr "The request could not be saved for recovery. This attempt was not sent. msgid "The retained inputs are unavailable for new evaluations." msgstr "The retained inputs are unavailable for new evaluations." -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:171 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:252 msgid "The rule list itself differs from the shipped rules." msgstr "The rule list itself differs from the shipped rules." @@ -2972,7 +3050,7 @@ msgstr "These scores compare original stored readings. Identical numerical resul msgid "These scores compare readings recomputed from retained waveforms. Shared-timestamp scores use observations available in every configuration. Differences describe agreement with the reference and do not establish validated improvement." msgstr "These scores compare readings recomputed from retained waveforms. Shared-timestamp scores use observations available in every configuration. Differences describe agreement with the reference and do not establish validated improvement." -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:237 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:267 msgid "This backtest compared stored readings, so there is no recomputed heart-rate trace to draw." msgstr "This backtest compared stored readings, so there is no recomputed heart-rate trace to draw." @@ -3083,16 +3161,16 @@ msgstr "Trace intervals {first}–{last} of {0}" msgid "Trace night" msgstr "Trace night" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:125 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:193 msgid "Tracking history length" msgstr "Tracking history length" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:115 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:183 msgid "Tracking tolerance (bpm)" msgstr "Tracking tolerance (bpm)" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:112 -#: src/components/biometrics/Calibration/CalibrationPage.tsx:206 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:127 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:221 msgid "tuning" msgstr "tuning" @@ -3115,7 +3193,7 @@ msgstr "Unavailable" msgid "Unavailable or belongs to a different sleeper." msgstr "Unavailable or belongs to a different sleeper." -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:53 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:121 msgid "unchanged" msgstr "unchanged" @@ -3154,8 +3232,8 @@ msgstr "Use the same sleeper identity as the reference records you intend to com msgid "Validated activation" msgstr "Validated activation" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:116 -#: src/components/biometrics/Calibration/CalibrationPage.tsx:206 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:131 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:221 msgid "validation" msgstr "validation" @@ -3209,12 +3287,11 @@ msgstr "Wake" msgid "Waveform dataset retained: {published}" msgstr "Waveform dataset retained: {published}" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:157 -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:180 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:196 msgid "What the saved profile did" msgstr "What the saved profile did" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:176 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:257 msgid "when" msgstr "when" @@ -3233,7 +3310,7 @@ msgstr "Window IBI variability index" msgid "Window IBI variability index (ms)" msgstr "Window IBI variability index (ms)" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:158 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:239 msgid "window IBI variability, not clinical RMSSD" msgstr "window IBI variability, not clinical RMSSD" diff --git a/src/lib/i18n/locales/es.po b/src/lib/i18n/locales/es.po index 2c683f25..192c1da0 100644 --- a/src/lib/i18n/locales/es.po +++ b/src/lib/i18n/locales/es.po @@ -13,15 +13,19 @@ msgstr "" "Language-Team: \n" "Plural-Forms: \n" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:169 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:250 msgid ", with {count} overrides" msgstr ", con {count} ajustes propios" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:169 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:250 msgid ", with 1 override" msgstr ", con 1 ajuste propio" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:369 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:250 +msgid ", with no overrides" +msgstr "" + +#: src/components/biometrics/Calibration/CalibrationPage.tsx:393 msgid ". Separate from the profile." msgstr ". Independiente del perfil." @@ -36,7 +40,7 @@ msgstr "{0} · {1} · revisión {2}" #. placeholder {1}: label.day #. placeholder {2}: fmt1(prev) #. placeholder {3}: fmt1(saved) -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:198 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:228 msgid "{0} {1}: previous {2}, saved {3} bpm" msgstr "{0} {1}: anterior {2}, guardado {3} lpm" @@ -44,12 +48,12 @@ msgstr "{0} {1}: anterior {2}, guardado {3} lpm" #. placeholder {1}: avg(n => n.previous.heart_rate.mae as number) #. placeholder {2}: avg(n => n.saved.heart_rate.mae as number) #. placeholder {3}: shortDuration(job.updatedAtMs - job.createdAtMs) -#: src/components/biometrics/Calibration/CalibrationPage.tsx:216 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:231 msgid "{0} HR error {1} → {2} bpm · {3}" msgstr "{0} error de FC {1} → {2} lpm · {3}" #. placeholder {0}: i.summary.accepted -#: src/components/biometrics/Calibration/CalibrationPage.tsx:227 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:242 msgid "{0} records" msgstr "{0} registros" @@ -69,15 +73,15 @@ msgstr "Una solicitud guardada no significa que la ejecución haya comenzado o f msgid "A saved revision is not evidence of validation or processor adoption." msgstr "Una revisión guardada no demuestra validación ni adopción por el procesador." -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:109 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:177 msgid "Accept an estimate when it is within" msgstr "Aceptar una estimación cuando esté a menos de" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:156 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:237 msgid "accepted 10-second IBI windows" msgstr "ventanas IBI de 10 segundos aceptadas" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:123 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:191 msgid "accepted estimates in the tracker" msgstr "estimaciones aceptadas en el seguimiento" @@ -216,7 +220,7 @@ msgid "Available reference records by metric (across revisions)" msgstr "Registros de referencia disponibles por métrica (entre revisiones)" #. placeholder {0}: group.latest.id.slice(0, 4) -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:181 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:197 msgid "Backtest {0}. Frozen dataset replayed through the previous and saved profiles; nothing here changes the pod." msgstr "Prueba retrospectiva {0}. El conjunto congelado se reprodujo con los perfiles anterior y guardado; nada aquí cambia el pod." @@ -225,7 +229,7 @@ msgid "Backtest jobs" msgstr "Trabajos de evaluación retrospectiva" #. placeholder {0}: splitLabel[job.split] -#: src/components/biometrics/Calibration/CalibrationPage.tsx:220 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:235 msgid "Backtest on {0} nights failed" msgstr "Falló la prueba retrospectiva en noches de {0}" @@ -258,19 +262,19 @@ msgstr "Hash de configuración anterior" msgid "Biometric calibration" msgstr "Calibración biométrica" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:264 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:279 msgid "Biometric calibration side" msgstr "Lado de la calibración biométrica" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:113 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:181 msgid "bpm of the tracker" msgstr "lpm del seguimiento" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:128 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:204 msgid "BREATHING" msgstr "RESPIRACIÓN" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:173 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:189 msgid "Breathing error" msgstr "Error de respiración" @@ -394,11 +398,11 @@ msgstr "Reglas de fases del sueño del candidato" msgid "Candidate spike threshold" msgstr "Umbral de picos del candidato" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:94 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:162 msgid "Changed values are white; the previous profile (System defaults) sits underneath. Moving a control edits the saved profile; saving again re-requests it. The processor adopts a profile only after it acknowledges a request." msgstr "Los valores cambiados aparecen en blanco; el perfil anterior (valores del sistema) queda debajo. Mover un control edita el perfil guardado; guardarlo de nuevo lo vuelve a solicitar. El procesador adopta un perfil solo después de confirmar una solicitud." -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:95 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:163 msgid "Changed values are white; the previous profile sits underneath. Moving a control edits the saved profile; saving again re-requests it. The processor adopts a profile only after it acknowledges a request." msgstr "Los valores cambiados aparecen en blanco; el perfil anterior queda debajo. Mover un control edita el perfil guardado; guardarlo de nuevo lo vuelve a solicitar. El procesador adopta un perfil solo después de confirmar una solicitud." @@ -426,6 +430,10 @@ msgstr "Elige una plantilla" msgid "Choose the protocol before looking at results. Creating an experiment does not run a job or apply a profile." msgstr "Elija el protocolo antes de ver los resultados. Crear un experimento no ejecuta un trabajo ni aplica un perfil." +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:78 +msgid "Choosing nights the reference covers…" +msgstr "" + #: src/components/biometrics/Calibration/ReferenceOverlay.tsx:38 msgid "Circles are recomputed readings; squares are reference points; horizontal bars are reference intervals. Only the two displayed pages are shown. This visual overlay does not create new matched pairs or scores." msgstr "Los círculos son lecturas recalculadas; los cuadrados son puntos de referencia; las barras horizontales son intervalos de referencia. Solo se muestran las dos páginas seleccionadas. Esta superposición visual no crea nuevos pares emparejados ni puntuaciones." @@ -434,7 +442,7 @@ msgstr "Los círculos son lecturas recalculadas; los cuadrados son puntos de ref msgid "Clear this night’s selection" msgstr "Borrar la selección de esta noche" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:187 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:205 msgid "click a night" msgstr "elige una noche" @@ -544,7 +552,7 @@ msgstr "Noches completadas" msgid "Compressed bytes: {0}" msgstr "Bytes comprimidos: {0}" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:372 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:396 msgid "config id" msgstr "id de configuración" @@ -586,11 +594,11 @@ msgstr "Cobertura reducida" msgid "Coverage reduced in at least one evaluated window" msgstr "Cobertura reducida en al menos una ventana evaluada" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:61 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:129 msgid "covered" msgstr "cubierto" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:361 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:380 msgid "current" msgstr "vigente" @@ -626,7 +634,7 @@ msgstr "Conjunto de datos" msgid "Dataset hash" msgstr "Hash del conjunto de datos" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:84 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:99 msgid "Dataset nights" msgstr "Noches del conjunto" @@ -661,7 +669,7 @@ msgid "Descriptive evaluation only" msgstr "Solo evaluación descriptiva" #. placeholder {0}: shortDuration(run.durationMs) -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:150 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:170 msgid "done · {0}" msgstr "listo · {0}" @@ -677,11 +685,15 @@ msgstr "Las duraciones abarcan el trabajo secuencial de Python hasta la generaci msgid "Each configuration on its own available intervals" msgstr "Cada configuración en sus propios intervalos disponibles" +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:268 +msgid "Each night's heart rate against the reference appears here once a backtest has run." +msgstr "" + #: src/components/biometrics/Calibration/ProfileLibrary.tsx:34 msgid "Edit a new candidate from this revision" msgstr "Editar un nuevo candidato a partir de esta revisión" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:192 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:273 msgid "Edit overrides" msgstr "Editar ajustes" @@ -744,7 +756,7 @@ msgstr "Estimar incertidumbre exploratoria por noches completas" msgid "Evaluated minutes: {0}" msgstr "Minutos evaluados: {0}" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:75 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:90 msgid "Evaluation dataset" msgstr "Conjunto de evaluación" @@ -812,7 +824,7 @@ msgstr "El estado de ejecución es independiente de la solidez de la evidencia b msgid "Experiment saved: {0}" msgstr "Experimento guardado: {0}" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:361 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:380 msgid "expired" msgstr "caducada" @@ -841,7 +853,7 @@ msgstr "Intervalo exploratorio estimado" msgid "F1" msgstr "F1" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:150 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:170 msgid "failed" msgstr "falló" @@ -851,7 +863,7 @@ msgstr "falló" msgid "Failed" msgstr "Fallido" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:184 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:265 msgid "Fallback" msgstr "Fase por defecto" @@ -860,16 +872,16 @@ msgstr "Fase por defecto" msgid "Fallback stage" msgstr "Fase alternativa" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:202 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:217 msgid "Fell back to a compatible profile" msgstr "Se recurrió a un perfil compatible" #. placeholder {0}: splitLabel[job.split] -#: src/components/biometrics/Calibration/CalibrationPage.tsx:220 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:235 msgid "Finished a backtest on {0} nights" msgstr "Terminó una prueba retrospectiva en noches de {0}" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:314 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:344 msgid "first 500 readings" msgstr "primeras 500 lecturas" @@ -897,7 +909,7 @@ msgstr "Desplazamiento fijo añadido a las fechas del pod (ms, ±3600000)" msgid "Flags" msgstr "Indicadores" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:128 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:143 msgid "Freeze a new dataset" msgstr "Congelar un conjunto nuevo" @@ -909,29 +921,41 @@ msgstr "Fijar datos de evaluación" msgid "Freeze selected inputs" msgstr "Fijar datos seleccionados" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:361 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:85 +msgid "Freezing failed." +msgstr "" + +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:79 +msgid "Freezing the nights…" +msgstr "" + +#: src/components/biometrics/Calibration/CalibrationPage.tsx:380 msgid "freshness unknown" msgstr "vigencia desconocida" #. placeholder {0}: d.manifest.splitProtocol.assignments.length -#: src/components/biometrics/Calibration/CalibrationPage.tsx:224 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:239 msgid "Froze a dataset of {0} nights" msgstr "Congeló un conjunto de {0} noches" #. placeholder {0}: formatDay(dataset.createdAtMs, timezone) #. placeholder {1}: formatBytes(dataset.byteLength) -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:79 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:94 msgid "frozen {0} · {1}" msgstr "congelado {0} · {1}" +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:84 +msgid "Frozen and queued. The backtest replays every split; results appear as each finishes." +msgstr "" + #. placeholder {0}: nights.length #. placeholder {1}: sources.join(' + ') -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:69 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:71 msgid "Frozen copy of {0} nights and {1} reference. Later edits or deletions do not touch it. Whole nights are split before scoring; a split is not evidence on its own." msgstr "Copia congelada de {0} noches y la referencia de {1}. Las ediciones o eliminaciones posteriores no la afectan. Las noches se reparten enteras antes de puntuar; un reparto no es evidencia por sí solo." #. placeholder {0}: nights.length -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:70 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:72 msgid "Frozen copy of {0} nights. Later edits or deletions do not touch it. Whole nights are split before scoring; a split is not evidence on its own." msgstr "Copia congelada de {0} noches. Las ediciones o eliminaciones posteriores no la afectan. Las noches se reparten enteras antes de puntuar; un reparto no es evidencia por sí solo." @@ -971,7 +995,7 @@ msgstr "Umbrales fijados de los objetivos de fases del sueño" msgid "Groups use native reference timestamps and record types, independently of candidate output. Half-window groups exclude intervals crossing the midpoint. Groups overlap and are not independent experiments. These are not motion or artifact subgroups." msgstr "Los grupos usan las marcas de tiempo y los tipos de registro originales de la referencia, independientemente de la salida del candidato. Los grupos de medio período excluyen los intervalos que cruzan el punto medio. Los grupos se solapan y no son experimentos independientes. No son subgrupos de movimiento ni de artefactos." -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:228 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:258 msgid "H holdout" msgstr "H reserva" @@ -983,13 +1007,13 @@ msgstr "El estado se muestreó en las comprobaciones de admisión, normalmente c msgid "Heart rate" msgstr "Frecuencia cardíaca" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:106 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:174 msgid "HEART RATE" msgstr "FRECUENCIA CARDÍACA" #. placeholder {0}: label.weekday #. placeholder {1}: label.day -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:291 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:321 msgid "Heart rate · {0} {1}" msgstr "Frecuencia cardíaca · {0} {1}" @@ -1003,7 +1027,7 @@ msgstr "Frecuencia cardíaca (latidos/min)" msgid "Heart-rate spike threshold (standard deviations)" msgstr "Umbral de picos de frecuencia cardíaca (desviaciones estándar)" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:295 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:325 msgid "Heart-rate trace" msgstr "Trazo de frecuencia cardíaca" @@ -1011,7 +1035,7 @@ msgstr "Trazo de frecuencia cardíaca" msgid "Hello, tRPC test page!" msgstr "¡Hola, tRPC test page!" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:377 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:401 msgid "hide" msgstr "ocultar" @@ -1022,8 +1046,8 @@ msgstr "ocultar" msgid "Higher agreement: {0}; lower: {1}; unchanged: {2}" msgstr "Mayor concordancia: {0}; menor: {1}; sin cambios: {2}" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:120 -#: src/components/biometrics/Calibration/CalibrationPage.tsx:206 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:135 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:221 msgid "holdout" msgstr "reserva" @@ -1040,23 +1064,23 @@ msgstr "Reserva" msgid "Holdout accesses recorded for this experiment: {0}" msgstr "Accesos a la reserva registrados para este experimento: {0}" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:258 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:288 msgid "Holdout is the number to trust; tuning nights were used to pick these values." msgstr "La reserva es la cifra fiable; las noches de ajuste se usaron para elegir estos valores." -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:170 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:186 msgid "HR error · holdout" msgstr "Error de FC · reserva" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:168 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:184 msgid "HR error · tuning" msgstr "Error de FC · ajuste" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:169 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:185 msgid "HR error · validation" msgstr "Error de FC · validación" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:186 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:204 msgid "HR error per night · bpm vs reference" msgstr "Error de FC por noche · lpm frente a la referencia" @@ -1068,11 +1092,11 @@ msgstr "Definición de VFC" msgid "HRV definitions differ or are partly unknown. A combined HRV average is unavailable." msgstr "Las definiciones de VFC difieren o se desconocen en parte. No se puede calcular un promedio combinado de VFC." -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:149 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:230 msgid "HRV INDEX" msgstr "ÍNDICE DE VFC" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:172 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:188 msgid "HRV index error" msgstr "Error del índice de VFC" @@ -1109,7 +1133,7 @@ msgstr "Importar los registros aceptados y omitir los rechazados" msgid "Import preview" msgstr "Vista previa de la importación" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:126 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:141 msgid "Import reference" msgstr "Importar referencia" @@ -1145,7 +1169,7 @@ msgid "Inherited default" msgstr "Valor predeterminado heredado" #. placeholder {0}: stages.ruleCount -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:166 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:247 msgid "Inherits the {0} rules and fallback from" msgstr "Hereda las {0} reglas y la fase por defecto de" @@ -1240,11 +1264,11 @@ msgstr "Conservar informe" msgid "Keep retained inputs" msgstr "Conservar los datos retenidos" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:318 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:337 msgid "Keep saved profile" msgstr "Mantener el perfil guardado" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:119 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:187 msgid "Keep the last" msgstr "Conservar las últimas" @@ -1280,7 +1304,11 @@ msgstr "Ligero" msgid "Limitations" msgstr "Limitaciones" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:144 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:77 +msgid "Linking Watch data already on the pod…" +msgstr "" + +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:223 msgid "Longest breathing interval (× median)" msgstr "Intervalo respiratorio más largo (× mediana)" @@ -1333,7 +1361,7 @@ msgstr "Error absoluto medio en marcas de tiempo comunes: activo {0}, candidato msgid "Measured Python processes: {0} of {1}" msgstr "Procesos Python medidos: {0} de {1}" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:366 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:390 msgid "Measured sensor calibration for this side:" msgstr "Calibración medida del sensor para este lado:" @@ -1365,11 +1393,15 @@ msgstr "Ventana de la métrica: {0} segundos." msgid "Metrics: {0}" msgstr "Métricas: {0}" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:202 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:217 msgid "Migrated sleep-stage settings into the profile" msgstr "Migró los ajustes de fases del sueño al perfil" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:159 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:201 +msgid "Minimum beat signal score" +msgstr "" + +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:240 msgid "Minimum IBI windows" msgstr "Ventanas IBI mínimas" @@ -1469,7 +1501,7 @@ msgstr "Siguiente página de la traza" msgid "Night {0}" msgstr "Noche {0}" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:171 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:187 msgid "Night covered" msgstr "Noche cubierta" @@ -1482,13 +1514,29 @@ msgid "No accepted time range." msgstr "No hay un intervalo de tiempo aceptado." #: src/components/biometrics/Calibration/CalibrationEvidence.tsx:158 -msgid "No backtest has replayed the saved profile yet. Saving a change re-runs the latest experiment on its tuning and validation nights; nothing here changes the pod." -msgstr "Ninguna prueba retrospectiva ha reproducido aún el perfil guardado. Guardar un cambio vuelve a ejecutar el último experimento en sus noches de ajuste y validación; nada aquí cambia el pod." +#~ msgid "No backtest has replayed the saved profile yet. Saving a change re-runs the latest experiment on its tuning and validation nights; nothing here changes the pod." +#~ msgstr "Ninguna prueba retrospectiva ha reproducido aún el perfil guardado. Guardar un cambio vuelve a ejecutar el último experimento en sus noches de ajuste y validación; nada aquí cambia el pod." + +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:177 +msgid "No backtest has replayed the saved profile yet. Saving a change replays the frozen dataset on its tuning, validation and holdout nights; nothing here changes the pod." +msgstr "" #: src/components/biometrics/Calibration/BacktestJobs.tsx:28 msgid "No backtest jobs for this side." msgstr "No hay trabajos de evaluación retrospectiva para este lado." +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:175 +msgid "No backtest yet. Freeze a new dataset below; it replays the newest referenced nights through the previous and saved profiles. Nothing here changes the pod." +msgstr "" + +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:174 +msgid "No backtest yet. Import a reference for this side, then freeze nights that have it; the replay scores the previous and saved profiles against it. Nothing here changes the pod." +msgstr "" + +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:176 +msgid "No backtest yet. This dataset has no waveform replay; freeze a new dataset below to set one up. Nothing here changes the pod." +msgstr "" + #: src/components/biometrics/Calibration/BacktestReport.tsx:38 msgid "No committed report is available yet." msgstr "Todavía no hay ningún informe guardado disponible." @@ -1506,8 +1554,12 @@ msgid "No coverage reduction detected" msgstr "No se detectó reducción de cobertura" #: src/components/biometrics/Calibration/CalibrationEvidence.tsx:71 -msgid "No dataset is frozen for this side yet. Freeze recorded nights with an imported reference to backtest a profile." -msgstr "Aún no hay un conjunto congelado para este lado. Congela noches grabadas con una referencia importada para probar un perfil." +#~ msgid "No dataset is frozen for this side yet. Freeze recorded nights with an imported reference to backtest a profile." +#~ msgstr "Aún no hay un conjunto congelado para este lado. Congela noches grabadas con una referencia importada para probar un perfil." + +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:73 +msgid "No dataset is frozen for this side yet. Freezing takes the newest nights a reference covers, with their RAW archives, so a backtest can replay them." +msgstr "" #: src/components/biometrics/Calibration/BacktestReport.tsx:62 #: src/components/biometrics/Calibration/NumericReport.tsx:28 @@ -1563,7 +1615,7 @@ msgstr "No hay ejecuciones prospectivas para este lado." msgid "No recorded nights for this side." msgstr "No hay noches registradas para este lado." -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:101 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:116 msgid "no reference" msgstr "sin referencia" @@ -1571,7 +1623,7 @@ msgstr "sin referencia" msgid "No reference" msgstr "Sin referencia" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:286 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:316 msgid "no reference device · excluded from error" msgstr "sin dispositivo de referencia · excluida del error" @@ -1635,7 +1687,7 @@ msgstr "Ninguno" msgid "Normalized reference measurements" msgstr "Mediciones de referencia normalizadas" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:362 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:386 msgid "not reported" msgstr "sin informar" @@ -1648,6 +1700,10 @@ msgstr "Sin información" msgid "Not requested" msgstr "No solicitado" +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:200 +msgid "not run" +msgstr "" + #: src/components/biometrics/Calibration/BacktestReport.tsx:63 #: src/components/biometrics/Calibration/NumericReport.tsx:24 #: src/components/biometrics/Calibration/NumericReport.tsx:25 @@ -1656,12 +1712,12 @@ msgstr "No solicitado" msgid "Not scored" msgstr "Sin puntuación" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:55 -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:57 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:123 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:125 msgid "not tested" msgstr "sin probar" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:330 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:349 msgid "Nothing changes until you move a control or restore." msgstr "Nada cambia hasta que muevas un control o restaures." @@ -1770,7 +1826,7 @@ msgstr "Perfiles anteriores" msgid "Only the current page is plotted. Dots represent individual readings; gaps and missing values are not interpolated. Original stored readings remain unchanged." msgstr "Solo se representa la página actual. Los puntos son lecturas individuales; los huecos y los valores faltantes no se interpolan. Las lecturas originales almacenadas no se modifican." -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:135 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:211 msgid "or longer than" msgstr "o más largos que" @@ -1821,7 +1877,7 @@ msgstr "La intención de personalización puede cambiar aunque el valor resuelto msgid "Overview" msgstr "Resumen" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:330 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:349 msgid "Overwrites the saved profile and requests it; the current one becomes previous." msgstr "Sobrescribe el perfil guardado y lo solicita; el actual pasa a ser el anterior." @@ -1870,7 +1926,7 @@ msgstr "Liberar datos permanentemente" msgid "Permanently release report" msgstr "Liberar informe permanentemente" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:238 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:268 msgid "Pick a scored night to see its heart-rate trace." msgstr "Elige una noche puntuada para ver su trazo de frecuencia cardíaca." @@ -1957,11 +2013,11 @@ msgstr "Vista previa de la importación" msgid "Preview normalized records before saving them on the pod." msgstr "Revise los registros normalizados antes de guardarlos en el pod." -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:222 -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:245 -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:312 -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:65 -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:141 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:252 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:275 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:342 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:133 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:217 msgid "previous" msgstr "anterior" @@ -1999,12 +2055,12 @@ msgid "Processor" msgstr "Procesador" #. placeholder {0}: event.generation -#: src/components/biometrics/Calibration/CalibrationPage.tsx:203 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:218 msgid "Processor acknowledged generation {0}" msgstr "El procesador confirmó la generación {0}" #. placeholder {0}: event.generation -#: src/components/biometrics/Calibration/CalibrationPage.tsx:204 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:219 msgid "Processor rejected generation {0}" msgstr "El procesador rechazó la generación {0}" @@ -2042,7 +2098,7 @@ msgstr "Observaciones prospectivas" msgid "Protocol hash" msgstr "Hash del protocolo" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:287 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:317 msgid "quality" msgstr "calidad" @@ -2054,7 +2110,7 @@ msgstr "Calidad" msgid "Queue backtest" msgstr "Poner evaluación retrospectiva en cola" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:150 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:170 msgid "queued" msgstr "en cola" @@ -2062,10 +2118,20 @@ msgstr "en cola" msgid "Queued" msgstr "En cola" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:65 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:83 +msgid "Queueing the backtest…" +msgstr "" + +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:133 msgid "range" msgstr "rango" +#. placeholder {0}: setup.done ?? 0 +#. placeholder {1}: setup.total ?? 0 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:80 +msgid "Reading RAW archives · {0} / {1}" +msgstr "" + #: src/components/biometrics/Calibration/StageReport.tsx:52 msgid "Recall" msgstr "Sensibilidad" @@ -2104,8 +2170,8 @@ msgstr "Configuraciones registradas: {0}; lecturas con configuración desconocid msgid "Recorded nights" msgstr "Noches registradas" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:313 -#: src/components/biometrics/Calibration/CalibrationPage.tsx:128 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:343 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:143 msgid "reference" msgstr "referencia" @@ -2241,7 +2307,7 @@ msgstr "Actualizar informe" msgid "Regression under the evaluation policy" msgstr "Empeoramiento según la política de evaluación" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:131 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:207 msgid "Reject intervals shorter than" msgstr "Rechazar intervalos más cortos que" @@ -2261,7 +2327,7 @@ msgstr "Liberar datos conservados" msgid "Release these retained inputs permanently? Existing reports and audit metadata remain. Active jobs must finish or be cancelled first." msgstr "¿Liberar estos datos conservados permanentemente? Se mantienen los informes existentes y los metadatos de auditoría. Los trabajos activos deben finalizar o cancelarse primero." -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:79 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:94 msgid "released" msgstr "liberado" @@ -2322,7 +2388,11 @@ msgstr "Hash del informe" msgid "Report identity" msgstr "Identidad del informe" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:152 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:197 +msgid "Report no heart rate when the beat signal scores below" +msgstr "" + +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:233 msgid "Report the index only after" msgstr "Informar el índice solo después de" @@ -2339,7 +2409,7 @@ msgid "Requested" msgstr "Solicitado" #. placeholder {0}: event.generation -#: src/components/biometrics/Calibration/CalibrationPage.tsx:198 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:213 msgid "Requested activation · generation {0}" msgstr "Solicitó la activación · generación {0}" @@ -2375,7 +2445,7 @@ msgstr "Reiniciar la exploración de archivos" msgid "Restore and request" msgstr "Restaurar y solicitar" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:326 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:345 msgid "Restore previous" msgstr "Restaurar el anterior" @@ -2398,11 +2468,11 @@ msgstr "Restaurar el perfil anterior" msgid "Restore… ▾" msgstr "Restaurar… ▾" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:201 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:216 msgid "Restored system defaults and requested them" msgstr "Restauró los valores del sistema y los solicitó" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:200 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:215 msgid "Restored the previous profile and requested it" msgstr "Restauró el perfil anterior y lo solicitó" @@ -2435,6 +2505,10 @@ msgstr "El servidor ha verificado la integridad de los datos conservados." msgid "Retained waveform replay" msgstr "Reprocesamiento de formas de onda conservadas" +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:81 +msgid "Retaining the archives…" +msgstr "" + #: src/components/biometrics/Calibration/ManualProfileActivation.tsx:62 #: src/components/biometrics/Calibration/ValidatedProfileActivation.tsx:46 msgid "Retry exact activation request" @@ -2489,11 +2563,11 @@ msgid "Row {0}: {1}" msgstr "Fila {0}: {1}" #. placeholder {0}: o.rule -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:174 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:255 msgid "Rule {0}" msgstr "Regla {0}" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:150 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:170 msgid "running" msgstr "en curso" @@ -2510,7 +2584,7 @@ msgstr "Pico de memoria muestreado del proceso de trabajo: {0} MiB. Los picos br msgid "Save a candidate for evaluation. This does not change live processing." msgstr "Guarde un candidato para evaluarlo. Esto no cambia el procesamiento en vivo." -#: src/components/biometrics/Calibration/CalibrationPage.tsx:318 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:337 msgid "Save and request" msgstr "Guardar y solicitar" @@ -2522,7 +2596,7 @@ msgstr "Guardar candidato sin aplicar" msgid "Save evaluation protocol" msgstr "Guardar protocolo de evaluación" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:174 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:189 msgid "Save failed." msgstr "No se pudo guardar." @@ -2530,9 +2604,9 @@ msgstr "No se pudo guardar." msgid "Save previewed import" msgstr "Guardar la importación revisada" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:226 -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:246 -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:311 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:256 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:276 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:341 msgid "saved" msgstr "guardado" @@ -2553,7 +2627,7 @@ msgstr "Experimentos guardados" msgid "Saved observations: {0}" msgstr "Observaciones guardadas: {0}" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:92 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:160 msgid "Saved profile against previous" msgstr "Perfil guardado frente al anterior" @@ -2567,7 +2641,7 @@ msgstr "Revisiones de perfiles guardadas" msgid "Saved sample times: {0}. Planned observation intervals: {1}." msgstr "Marcas de tiempo de muestras guardadas: {0}. Intervalos de observación previstos: {1}." -#: src/components/biometrics/Calibration/CalibrationPage.tsx:196 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:211 msgid "Saved the profile" msgstr "Guardó el perfil" @@ -2575,7 +2649,7 @@ msgstr "Guardó el perfil" msgid "Saves it as the profile and requests it; the current saved profile becomes previous. The processor keeps running what it has until it acknowledges the request." msgstr "Lo guarda como perfil y lo solicita; el perfil guardado actual pasa a ser el anterior. El procesador sigue con lo que tiene hasta confirmar la solicitud." -#: src/components/biometrics/Calibration/CalibrationPage.tsx:318 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:337 msgid "Saving…" msgstr "Guardando…" @@ -2654,6 +2728,10 @@ msgstr "Sesiones seleccionadas: {0} / 60" msgid "Sessions: {0}" msgstr "Sesiones: {0}" +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:82 +msgid "Setting up the replay…" +msgstr "" + #: src/components/biometrics/Calibration/NumericReport.tsx:108 msgid "Shared MAE difference" msgstr "Diferencia de MAE con observaciones comunes" @@ -2676,11 +2754,11 @@ msgstr "Este informe no dispone de comparaciones en marcas de tiempo comunes." msgid "Shared-timestamp comparisons. Differences are comparison minus baseline; negative error differences mean lower error on shared observations." msgstr "Comparaciones en marcas de tiempo comunes. Las diferencias se calculan como comparación menos base; las diferencias negativas de error indican un menor error en las observaciones comunes." -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:143 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:222 msgid "Shortest breathing interval (× median)" msgstr "Intervalo respiratorio más corto (× mediana)" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:377 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:401 msgid "show" msgstr "mostrar" @@ -2705,11 +2783,11 @@ msgstr "Sesgo con signo" msgid "Simulated demo: generated readings, no hardware execution or qualification." msgstr "Demostración simulada: lecturas generadas, sin ejecución en hardware ni cualificación." -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:168 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:249 msgid "Sleep stages" msgstr "Fases del sueño" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:162 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:243 msgid "SLEEP STAGES" msgstr "FASES DEL SUEÑO" @@ -2826,7 +2904,7 @@ msgid "Stop recorded" msgstr "Parada registrada" #. placeholder {0}: i.summary.source -#: src/components/biometrics/Calibration/CalibrationPage.tsx:227 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:242 msgid "Stored a {0} reference import" msgstr "Guardó una importación de referencia de {0}" @@ -2848,7 +2926,7 @@ msgstr "Comparación de lecturas almacenadas disponible" #: src/components/biometrics/Calibration/BacktestJobs.tsx:64 #: src/components/biometrics/Calibration/BacktestReport.tsx:60 #: src/components/biometrics/Calibration/CalibrationControls.tsx:80 -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:103 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:171 #: src/components/biometrics/Calibration/NumericReport.tsx:26 #: src/components/biometrics/Calibration/NumericTrace.tsx:147 #: src/components/biometrics/Calibration/ProfileComparison.tsx:29 @@ -2858,7 +2936,7 @@ msgstr "Comparación de lecturas almacenadas disponible" msgid "System defaults" msgstr "Valores predeterminados del sistema" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:54 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:122 msgid "tested on save" msgstr "se prueba al guardar" @@ -2879,7 +2957,7 @@ msgstr "El desfase fijado de {clockOffsetMs} ms se añade a las marcas de tiempo msgid "The last archive was unavailable. Continue to inspect the next archive." msgstr "El último archivo no estaba disponible. Continúa con la inspección del siguiente archivo." -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:139 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:215 msgid "the median" msgstr "la mediana" @@ -2912,7 +2990,7 @@ msgstr "No se pudo guardar la solicitud para recuperarla. Este intento no se env msgid "The retained inputs are unavailable for new evaluations." msgstr "Los datos conservados no están disponibles para nuevas evaluaciones." -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:171 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:252 msgid "The rule list itself differs from the shipped rules." msgstr "La lista de reglas difiere de las reglas de fábrica." @@ -2972,7 +3050,7 @@ msgstr "Estas puntuaciones comparan las lecturas originales almacenadas. Los res msgid "These scores compare readings recomputed from retained waveforms. Shared-timestamp scores use observations available in every configuration. Differences describe agreement with the reference and do not establish validated improvement." msgstr "Estas puntuaciones comparan lecturas recalculadas a partir de formas de onda conservadas. Las puntuaciones en marcas de tiempo comunes usan observaciones disponibles en todas las configuraciones. Las diferencias describen la concordancia con la referencia y no demuestran una mejora validada." -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:237 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:267 msgid "This backtest compared stored readings, so there is no recomputed heart-rate trace to draw." msgstr "Esta prueba comparó lecturas almacenadas, así que no hay un trazo de frecuencia cardíaca recalculado que dibujar." @@ -3083,16 +3161,16 @@ msgstr "Intervalos de la traza {first}–{last} de {0}" msgid "Trace night" msgstr "Noche de la traza" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:125 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:193 msgid "Tracking history length" msgstr "Longitud del historial de seguimiento" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:115 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:183 msgid "Tracking tolerance (bpm)" msgstr "Tolerancia de seguimiento (lpm)" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:112 -#: src/components/biometrics/Calibration/CalibrationPage.tsx:206 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:127 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:221 msgid "tuning" msgstr "ajuste" @@ -3115,7 +3193,7 @@ msgstr "No disponible" msgid "Unavailable or belongs to a different sleeper." msgstr "No disponible o pertenece a otra persona." -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:53 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:121 msgid "unchanged" msgstr "sin cambios" @@ -3154,8 +3232,8 @@ msgstr "Usa la misma identidad de la persona que duerme que en los registros de msgid "Validated activation" msgstr "Activación validada" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:116 -#: src/components/biometrics/Calibration/CalibrationPage.tsx:206 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:131 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:221 msgid "validation" msgstr "validación" @@ -3209,12 +3287,11 @@ msgstr "Vigilia" msgid "Waveform dataset retained: {published}" msgstr "Conjunto de datos de formas de onda conservado: {published}" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:157 -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:180 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:196 msgid "What the saved profile did" msgstr "Qué hizo el perfil guardado" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:176 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:257 msgid "when" msgstr "cuando" @@ -3233,7 +3310,7 @@ msgstr "Índice de variabilidad IBI por ventana" msgid "Window IBI variability index (ms)" msgstr "Índice de variabilidad IBI por ventanas (ms)" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:158 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:239 msgid "window IBI variability, not clinical RMSSD" msgstr "variabilidad IBI por ventana, no RMSSD clínico" diff --git a/src/lib/i18n/locales/pseudo.po b/src/lib/i18n/locales/pseudo.po index 90f5d980..3cbab978 100644 --- a/src/lib/i18n/locales/pseudo.po +++ b/src/lib/i18n/locales/pseudo.po @@ -13,15 +13,19 @@ msgstr "" "Language-Team: \n" "Plural-Forms: \n" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:169 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:250 msgid ", with {count} overrides" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:169 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:250 msgid ", with 1 override" msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:369 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:250 +msgid ", with no overrides" +msgstr "" + +#: src/components/biometrics/Calibration/CalibrationPage.tsx:393 msgid ". Separate from the profile." msgstr "" @@ -36,7 +40,7 @@ msgstr "" #. placeholder {1}: label.day #. placeholder {2}: fmt1(prev) #. placeholder {3}: fmt1(saved) -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:198 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:228 msgid "{0} {1}: previous {2}, saved {3} bpm" msgstr "" @@ -44,12 +48,12 @@ msgstr "" #. placeholder {1}: avg(n => n.previous.heart_rate.mae as number) #. placeholder {2}: avg(n => n.saved.heart_rate.mae as number) #. placeholder {3}: shortDuration(job.updatedAtMs - job.createdAtMs) -#: src/components/biometrics/Calibration/CalibrationPage.tsx:216 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:231 msgid "{0} HR error {1} → {2} bpm · {3}" msgstr "" #. placeholder {0}: i.summary.accepted -#: src/components/biometrics/Calibration/CalibrationPage.tsx:227 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:242 msgid "{0} records" msgstr "" @@ -69,15 +73,15 @@ msgstr "" msgid "A saved revision is not evidence of validation or processor adoption." msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:109 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:177 msgid "Accept an estimate when it is within" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:156 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:237 msgid "accepted 10-second IBI windows" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:123 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:191 msgid "accepted estimates in the tracker" msgstr "" @@ -216,7 +220,7 @@ msgid "Available reference records by metric (across revisions)" msgstr "" #. placeholder {0}: group.latest.id.slice(0, 4) -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:181 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:197 msgid "Backtest {0}. Frozen dataset replayed through the previous and saved profiles; nothing here changes the pod." msgstr "" @@ -225,7 +229,7 @@ msgid "Backtest jobs" msgstr "" #. placeholder {0}: splitLabel[job.split] -#: src/components/biometrics/Calibration/CalibrationPage.tsx:220 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:235 msgid "Backtest on {0} nights failed" msgstr "" @@ -258,19 +262,19 @@ msgstr "" msgid "Biometric calibration" msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:264 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:279 msgid "Biometric calibration side" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:113 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:181 msgid "bpm of the tracker" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:128 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:204 msgid "BREATHING" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:173 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:189 msgid "Breathing error" msgstr "" @@ -394,11 +398,11 @@ msgstr "" msgid "Candidate spike threshold" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:94 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:162 msgid "Changed values are white; the previous profile (System defaults) sits underneath. Moving a control edits the saved profile; saving again re-requests it. The processor adopts a profile only after it acknowledges a request." msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:95 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:163 msgid "Changed values are white; the previous profile sits underneath. Moving a control edits the saved profile; saving again re-requests it. The processor adopts a profile only after it acknowledges a request." msgstr "" @@ -426,6 +430,10 @@ msgstr "" msgid "Choose the protocol before looking at results. Creating an experiment does not run a job or apply a profile." msgstr "" +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:78 +msgid "Choosing nights the reference covers…" +msgstr "" + #: src/components/biometrics/Calibration/ReferenceOverlay.tsx:38 msgid "Circles are recomputed readings; squares are reference points; horizontal bars are reference intervals. Only the two displayed pages are shown. This visual overlay does not create new matched pairs or scores." msgstr "" @@ -434,7 +442,7 @@ msgstr "" msgid "Clear this night’s selection" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:187 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:205 msgid "click a night" msgstr "" @@ -544,7 +552,7 @@ msgstr "" msgid "Compressed bytes: {0}" msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:372 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:396 msgid "config id" msgstr "" @@ -586,11 +594,11 @@ msgstr "" msgid "Coverage reduced in at least one evaluated window" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:61 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:129 msgid "covered" msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:361 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:380 msgid "current" msgstr "" @@ -626,7 +634,7 @@ msgstr "" msgid "Dataset hash" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:84 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:99 msgid "Dataset nights" msgstr "" @@ -661,7 +669,7 @@ msgid "Descriptive evaluation only" msgstr "" #. placeholder {0}: shortDuration(run.durationMs) -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:150 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:170 msgid "done · {0}" msgstr "" @@ -677,11 +685,15 @@ msgstr "" msgid "Each configuration on its own available intervals" msgstr "" +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:268 +msgid "Each night's heart rate against the reference appears here once a backtest has run." +msgstr "" + #: src/components/biometrics/Calibration/ProfileLibrary.tsx:34 msgid "Edit a new candidate from this revision" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:192 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:273 msgid "Edit overrides" msgstr "" @@ -744,7 +756,7 @@ msgstr "" msgid "Evaluated minutes: {0}" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:75 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:90 msgid "Evaluation dataset" msgstr "" @@ -812,7 +824,7 @@ msgstr "" msgid "Experiment saved: {0}" msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:361 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:380 msgid "expired" msgstr "" @@ -841,7 +853,7 @@ msgstr "" msgid "F1" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:150 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:170 msgid "failed" msgstr "" @@ -851,7 +863,7 @@ msgstr "" msgid "Failed" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:184 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:265 msgid "Fallback" msgstr "" @@ -860,16 +872,16 @@ msgstr "" msgid "Fallback stage" msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:202 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:217 msgid "Fell back to a compatible profile" msgstr "" #. placeholder {0}: splitLabel[job.split] -#: src/components/biometrics/Calibration/CalibrationPage.tsx:220 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:235 msgid "Finished a backtest on {0} nights" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:314 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:344 msgid "first 500 readings" msgstr "" @@ -897,7 +909,7 @@ msgstr "" msgid "Flags" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:128 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:143 msgid "Freeze a new dataset" msgstr "" @@ -909,29 +921,41 @@ msgstr "" msgid "Freeze selected inputs" msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:361 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:85 +msgid "Freezing failed." +msgstr "" + +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:79 +msgid "Freezing the nights…" +msgstr "" + +#: src/components/biometrics/Calibration/CalibrationPage.tsx:380 msgid "freshness unknown" msgstr "" #. placeholder {0}: d.manifest.splitProtocol.assignments.length -#: src/components/biometrics/Calibration/CalibrationPage.tsx:224 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:239 msgid "Froze a dataset of {0} nights" msgstr "" #. placeholder {0}: formatDay(dataset.createdAtMs, timezone) #. placeholder {1}: formatBytes(dataset.byteLength) -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:79 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:94 msgid "frozen {0} · {1}" msgstr "" +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:84 +msgid "Frozen and queued. The backtest replays every split; results appear as each finishes." +msgstr "" + #. placeholder {0}: nights.length #. placeholder {1}: sources.join(' + ') -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:69 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:71 msgid "Frozen copy of {0} nights and {1} reference. Later edits or deletions do not touch it. Whole nights are split before scoring; a split is not evidence on its own." msgstr "" #. placeholder {0}: nights.length -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:70 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:72 msgid "Frozen copy of {0} nights. Later edits or deletions do not touch it. Whole nights are split before scoring; a split is not evidence on its own." msgstr "" @@ -971,7 +995,7 @@ msgstr "" msgid "Groups use native reference timestamps and record types, independently of candidate output. Half-window groups exclude intervals crossing the midpoint. Groups overlap and are not independent experiments. These are not motion or artifact subgroups." msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:228 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:258 msgid "H holdout" msgstr "" @@ -983,13 +1007,13 @@ msgstr "" msgid "Heart rate" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:106 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:174 msgid "HEART RATE" msgstr "" #. placeholder {0}: label.weekday #. placeholder {1}: label.day -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:291 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:321 msgid "Heart rate · {0} {1}" msgstr "" @@ -1003,7 +1027,7 @@ msgstr "" msgid "Heart-rate spike threshold (standard deviations)" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:295 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:325 msgid "Heart-rate trace" msgstr "" @@ -1011,7 +1035,7 @@ msgstr "" msgid "Hello, tRPC test page!" msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:377 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:401 msgid "hide" msgstr "" @@ -1022,8 +1046,8 @@ msgstr "" msgid "Higher agreement: {0}; lower: {1}; unchanged: {2}" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:120 -#: src/components/biometrics/Calibration/CalibrationPage.tsx:206 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:135 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:221 msgid "holdout" msgstr "" @@ -1040,23 +1064,23 @@ msgstr "" msgid "Holdout accesses recorded for this experiment: {0}" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:258 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:288 msgid "Holdout is the number to trust; tuning nights were used to pick these values." msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:170 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:186 msgid "HR error · holdout" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:168 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:184 msgid "HR error · tuning" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:169 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:185 msgid "HR error · validation" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:186 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:204 msgid "HR error per night · bpm vs reference" msgstr "" @@ -1068,11 +1092,11 @@ msgstr "" msgid "HRV definitions differ or are partly unknown. A combined HRV average is unavailable." msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:149 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:230 msgid "HRV INDEX" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:172 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:188 msgid "HRV index error" msgstr "" @@ -1109,7 +1133,7 @@ msgstr "" msgid "Import preview" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:126 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:141 msgid "Import reference" msgstr "" @@ -1145,7 +1169,7 @@ msgid "Inherited default" msgstr "" #. placeholder {0}: stages.ruleCount -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:166 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:247 msgid "Inherits the {0} rules and fallback from" msgstr "" @@ -1240,11 +1264,11 @@ msgstr "" msgid "Keep retained inputs" msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:318 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:337 msgid "Keep saved profile" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:119 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:187 msgid "Keep the last" msgstr "" @@ -1280,7 +1304,11 @@ msgstr "" msgid "Limitations" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:144 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:77 +msgid "Linking Watch data already on the pod…" +msgstr "" + +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:223 msgid "Longest breathing interval (× median)" msgstr "" @@ -1333,7 +1361,7 @@ msgstr "" msgid "Measured Python processes: {0} of {1}" msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:366 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:390 msgid "Measured sensor calibration for this side:" msgstr "" @@ -1365,11 +1393,15 @@ msgstr "" msgid "Metrics: {0}" msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:202 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:217 msgid "Migrated sleep-stage settings into the profile" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:159 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:201 +msgid "Minimum beat signal score" +msgstr "" + +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:240 msgid "Minimum IBI windows" msgstr "" @@ -1469,7 +1501,7 @@ msgstr "" msgid "Night {0}" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:171 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:187 msgid "Night covered" msgstr "" @@ -1482,13 +1514,29 @@ msgid "No accepted time range." msgstr "" #: src/components/biometrics/Calibration/CalibrationEvidence.tsx:158 -msgid "No backtest has replayed the saved profile yet. Saving a change re-runs the latest experiment on its tuning and validation nights; nothing here changes the pod." +#~ msgid "No backtest has replayed the saved profile yet. Saving a change re-runs the latest experiment on its tuning and validation nights; nothing here changes the pod." +#~ msgstr "" + +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:177 +msgid "No backtest has replayed the saved profile yet. Saving a change replays the frozen dataset on its tuning, validation and holdout nights; nothing here changes the pod." msgstr "" #: src/components/biometrics/Calibration/BacktestJobs.tsx:28 msgid "No backtest jobs for this side." msgstr "" +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:175 +msgid "No backtest yet. Freeze a new dataset below; it replays the newest referenced nights through the previous and saved profiles. Nothing here changes the pod." +msgstr "" + +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:174 +msgid "No backtest yet. Import a reference for this side, then freeze nights that have it; the replay scores the previous and saved profiles against it. Nothing here changes the pod." +msgstr "" + +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:176 +msgid "No backtest yet. This dataset has no waveform replay; freeze a new dataset below to set one up. Nothing here changes the pod." +msgstr "" + #: src/components/biometrics/Calibration/BacktestReport.tsx:38 msgid "No committed report is available yet." msgstr "" @@ -1506,7 +1554,11 @@ msgid "No coverage reduction detected" msgstr "" #: src/components/biometrics/Calibration/CalibrationEvidence.tsx:71 -msgid "No dataset is frozen for this side yet. Freeze recorded nights with an imported reference to backtest a profile." +#~ msgid "No dataset is frozen for this side yet. Freeze recorded nights with an imported reference to backtest a profile." +#~ msgstr "" + +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:73 +msgid "No dataset is frozen for this side yet. Freezing takes the newest nights a reference covers, with their RAW archives, so a backtest can replay them." msgstr "" #: src/components/biometrics/Calibration/BacktestReport.tsx:62 @@ -1563,7 +1615,7 @@ msgstr "" msgid "No recorded nights for this side." msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:101 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:116 msgid "no reference" msgstr "" @@ -1571,7 +1623,7 @@ msgstr "" msgid "No reference" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:286 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:316 msgid "no reference device · excluded from error" msgstr "" @@ -1635,7 +1687,7 @@ msgstr "" msgid "Normalized reference measurements" msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:362 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:386 msgid "not reported" msgstr "" @@ -1648,6 +1700,10 @@ msgstr "" msgid "Not requested" msgstr "" +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:200 +msgid "not run" +msgstr "" + #: src/components/biometrics/Calibration/BacktestReport.tsx:63 #: src/components/biometrics/Calibration/NumericReport.tsx:24 #: src/components/biometrics/Calibration/NumericReport.tsx:25 @@ -1656,12 +1712,12 @@ msgstr "" msgid "Not scored" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:55 -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:57 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:123 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:125 msgid "not tested" msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:330 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:349 msgid "Nothing changes until you move a control or restore." msgstr "" @@ -1770,7 +1826,7 @@ msgstr "" msgid "Only the current page is plotted. Dots represent individual readings; gaps and missing values are not interpolated. Original stored readings remain unchanged." msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:135 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:211 msgid "or longer than" msgstr "" @@ -1821,7 +1877,7 @@ msgstr "" msgid "Overview" msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:330 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:349 msgid "Overwrites the saved profile and requests it; the current one becomes previous." msgstr "" @@ -1870,7 +1926,7 @@ msgstr "" msgid "Permanently release report" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:238 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:268 msgid "Pick a scored night to see its heart-rate trace." msgstr "" @@ -1957,11 +2013,11 @@ msgstr "" msgid "Preview normalized records before saving them on the pod." msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:222 -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:245 -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:312 -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:65 -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:141 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:252 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:275 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:342 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:133 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:217 msgid "previous" msgstr "" @@ -1999,12 +2055,12 @@ msgid "Processor" msgstr "" #. placeholder {0}: event.generation -#: src/components/biometrics/Calibration/CalibrationPage.tsx:203 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:218 msgid "Processor acknowledged generation {0}" msgstr "" #. placeholder {0}: event.generation -#: src/components/biometrics/Calibration/CalibrationPage.tsx:204 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:219 msgid "Processor rejected generation {0}" msgstr "" @@ -2042,7 +2098,7 @@ msgstr "" msgid "Protocol hash" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:287 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:317 msgid "quality" msgstr "" @@ -2054,7 +2110,7 @@ msgstr "" msgid "Queue backtest" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:150 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:170 msgid "queued" msgstr "" @@ -2062,10 +2118,20 @@ msgstr "" msgid "Queued" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:65 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:83 +msgid "Queueing the backtest…" +msgstr "" + +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:133 msgid "range" msgstr "" +#. placeholder {0}: setup.done ?? 0 +#. placeholder {1}: setup.total ?? 0 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:80 +msgid "Reading RAW archives · {0} / {1}" +msgstr "" + #: src/components/biometrics/Calibration/StageReport.tsx:52 msgid "Recall" msgstr "" @@ -2104,8 +2170,8 @@ msgstr "" msgid "Recorded nights" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:313 -#: src/components/biometrics/Calibration/CalibrationPage.tsx:128 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:343 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:143 msgid "reference" msgstr "" @@ -2241,7 +2307,7 @@ msgstr "" msgid "Regression under the evaluation policy" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:131 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:207 msgid "Reject intervals shorter than" msgstr "" @@ -2261,7 +2327,7 @@ msgstr "" msgid "Release these retained inputs permanently? Existing reports and audit metadata remain. Active jobs must finish or be cancelled first." msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:79 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:94 msgid "released" msgstr "" @@ -2322,7 +2388,11 @@ msgstr "" msgid "Report identity" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:152 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:197 +msgid "Report no heart rate when the beat signal scores below" +msgstr "" + +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:233 msgid "Report the index only after" msgstr "" @@ -2339,7 +2409,7 @@ msgid "Requested" msgstr "" #. placeholder {0}: event.generation -#: src/components/biometrics/Calibration/CalibrationPage.tsx:198 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:213 msgid "Requested activation · generation {0}" msgstr "" @@ -2375,7 +2445,7 @@ msgstr "" msgid "Restore and request" msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:326 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:345 msgid "Restore previous" msgstr "" @@ -2398,11 +2468,11 @@ msgstr "" msgid "Restore… ▾" msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:201 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:216 msgid "Restored system defaults and requested them" msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:200 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:215 msgid "Restored the previous profile and requested it" msgstr "" @@ -2435,6 +2505,10 @@ msgstr "" msgid "Retained waveform replay" msgstr "" +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:81 +msgid "Retaining the archives…" +msgstr "" + #: src/components/biometrics/Calibration/ManualProfileActivation.tsx:62 #: src/components/biometrics/Calibration/ValidatedProfileActivation.tsx:46 msgid "Retry exact activation request" @@ -2489,11 +2563,11 @@ msgid "Row {0}: {1}" msgstr "" #. placeholder {0}: o.rule -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:174 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:255 msgid "Rule {0}" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:150 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:170 msgid "running" msgstr "" @@ -2510,7 +2584,7 @@ msgstr "" msgid "Save a candidate for evaluation. This does not change live processing." msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:318 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:337 msgid "Save and request" msgstr "" @@ -2522,7 +2596,7 @@ msgstr "" msgid "Save evaluation protocol" msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:174 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:189 msgid "Save failed." msgstr "" @@ -2530,9 +2604,9 @@ msgstr "" msgid "Save previewed import" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:226 -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:246 -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:311 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:256 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:276 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:341 msgid "saved" msgstr "" @@ -2553,7 +2627,7 @@ msgstr "" msgid "Saved observations: {0}" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:92 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:160 msgid "Saved profile against previous" msgstr "" @@ -2567,7 +2641,7 @@ msgstr "" msgid "Saved sample times: {0}. Planned observation intervals: {1}." msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:196 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:211 msgid "Saved the profile" msgstr "" @@ -2575,7 +2649,7 @@ msgstr "" msgid "Saves it as the profile and requests it; the current saved profile becomes previous. The processor keeps running what it has until it acknowledges the request." msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:318 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:337 msgid "Saving…" msgstr "" @@ -2654,6 +2728,10 @@ msgstr "" msgid "Sessions: {0}" msgstr "" +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:82 +msgid "Setting up the replay…" +msgstr "" + #: src/components/biometrics/Calibration/NumericReport.tsx:108 msgid "Shared MAE difference" msgstr "" @@ -2676,11 +2754,11 @@ msgstr "" msgid "Shared-timestamp comparisons. Differences are comparison minus baseline; negative error differences mean lower error on shared observations." msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:143 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:222 msgid "Shortest breathing interval (× median)" msgstr "" -#: src/components/biometrics/Calibration/CalibrationPage.tsx:377 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:401 msgid "show" msgstr "" @@ -2705,11 +2783,11 @@ msgstr "" msgid "Simulated demo: generated readings, no hardware execution or qualification." msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:168 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:249 msgid "Sleep stages" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:162 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:243 msgid "SLEEP STAGES" msgstr "" @@ -2826,7 +2904,7 @@ msgid "Stop recorded" msgstr "" #. placeholder {0}: i.summary.source -#: src/components/biometrics/Calibration/CalibrationPage.tsx:227 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:242 msgid "Stored a {0} reference import" msgstr "" @@ -2848,7 +2926,7 @@ msgstr "" #: src/components/biometrics/Calibration/BacktestJobs.tsx:64 #: src/components/biometrics/Calibration/BacktestReport.tsx:60 #: src/components/biometrics/Calibration/CalibrationControls.tsx:80 -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:103 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:171 #: src/components/biometrics/Calibration/NumericReport.tsx:26 #: src/components/biometrics/Calibration/NumericTrace.tsx:147 #: src/components/biometrics/Calibration/ProfileComparison.tsx:29 @@ -2858,7 +2936,7 @@ msgstr "" msgid "System defaults" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:54 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:122 msgid "tested on save" msgstr "" @@ -2879,7 +2957,7 @@ msgstr "" msgid "The last archive was unavailable. Continue to inspect the next archive." msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:139 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:215 msgid "the median" msgstr "" @@ -2912,7 +2990,7 @@ msgstr "" msgid "The retained inputs are unavailable for new evaluations." msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:171 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:252 msgid "The rule list itself differs from the shipped rules." msgstr "" @@ -2972,7 +3050,7 @@ msgstr "" msgid "These scores compare readings recomputed from retained waveforms. Shared-timestamp scores use observations available in every configuration. Differences describe agreement with the reference and do not establish validated improvement." msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:237 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:267 msgid "This backtest compared stored readings, so there is no recomputed heart-rate trace to draw." msgstr "" @@ -3083,16 +3161,16 @@ msgstr "" msgid "Trace night" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:125 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:193 msgid "Tracking history length" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:115 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:183 msgid "Tracking tolerance (bpm)" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:112 -#: src/components/biometrics/Calibration/CalibrationPage.tsx:206 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:127 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:221 msgid "tuning" msgstr "" @@ -3115,7 +3193,7 @@ msgstr "" msgid "Unavailable or belongs to a different sleeper." msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:53 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:121 msgid "unchanged" msgstr "" @@ -3154,8 +3232,8 @@ msgstr "" msgid "Validated activation" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:116 -#: src/components/biometrics/Calibration/CalibrationPage.tsx:206 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:131 +#: src/components/biometrics/Calibration/CalibrationPage.tsx:221 msgid "validation" msgstr "" @@ -3209,12 +3287,11 @@ msgstr "" msgid "Waveform dataset retained: {published}" msgstr "" -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:157 -#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:180 +#: src/components/biometrics/Calibration/CalibrationEvidence.tsx:196 msgid "What the saved profile did" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:176 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:257 msgid "when" msgstr "" @@ -3233,7 +3310,7 @@ msgstr "" msgid "Window IBI variability index (ms)" msgstr "" -#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:158 +#: src/components/biometrics/Calibration/CalibrationProfileCard.tsx:239 msgid "window IBI variability, not clinical RMSSD" msgstr ""