Skip to content
This repository was archived by the owner on Jun 16, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 91 additions & 38 deletions GooseSwift/HealthDataStore+Snapshots.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,102 @@ import UIKit

extension HealthDataStore {
func runPacketScores() {
packetScoreStatus = "Extracting bridge packet-derived scores..."
let baseArgs = bridgeBaseArgs(requireTrustedEvidence: false)
do {
packetScoreReports["sleep"] = try sleepScoreReport(baseArgs: baseArgs)
refreshPrimarySleepFromScoreReport()
packetScoreReports["strain"] = try bridge.request(
method: "metrics.strain_score_from_features",
args: baseArgs.merging([
"resting_start": "0000",
"resting_end": "9999",
"resting_baseline_min_days": 3,
]) { _, new in new }
)
packetScoreReports["recovery"] = try bridge.request(
method: "metrics.recovery_score_from_features",
args: baseArgs.merging(recoveryScoreBridgeArgs()) { _, new in new }
)
packetScoreReports["stress"] = try bridge.request(
method: "metrics.stress_score_from_features",
args: baseArgs.merging([
"resting_start": "0000",
"resting_end": "9999",
"hrv_start": "0000",
"hrv_end": "9999",
"hrv_baseline_start": "0000",
"hrv_baseline_end": "9999",
"resting_baseline_min_days": 3,
"hrv_min_rr_intervals_to_compute": 2,
"hrv_baseline_min_days": 3,
]) { _, new in new }
)
packetScoreStatus = "Bridge packet-derived scores recomputed"
} catch {
packetScoreStatus = "Bridge score run blocked: \(Self.shortError(error))"
let recoveryArgs = baseArgs.merging(recoveryScoreBridgeArgs()) { _, new in new }
let strainArgs = baseArgs.merging([
"resting_start": "0000",
"resting_end": "9999",
"resting_baseline_min_days": 3,
]) { _, new in new }
let stressArgs = baseArgs.merging([
"resting_start": "0000",
"resting_end": "9999",
"hrv_start": "0000",
"hrv_end": "9999",
"hrv_baseline_start": "0000",
"hrv_baseline_end": "9999",
"resting_baseline_min_days": 3,
"hrv_min_rr_intervals_to_compute": 2,
"hrv_baseline_min_days": 3,
]) { _, new in new }

let bridge = self.bridge
packetInputQueue.async { [weak self] in
do {
let sleepReport = try bridge.request(
method: "metrics.sleep_score_from_features",
args: baseArgs.merging([
"sleep_need_minutes": 480.0,
"low_motion_threshold_0_to_1": 0.05,
"disturbance_motion_threshold_0_to_1": 0.20,
"target_midpoint_minutes_since_midnight": 180.0,
"history_import_in_progress": false,
"algorithm_id": "goose.sleep.v1",
]) { _, new in new }
)
let strainReport = try bridge.request(
method: "metrics.strain_score_from_features",
args: strainArgs
)
let recoveryReport = try bridge.request(
method: "metrics.recovery_score_from_features",
args: recoveryArgs
)
let stressReport = try bridge.request(
method: "metrics.stress_score_from_features",
args: stressArgs
)

DispatchQueue.main.async { [weak self] in
guard let self else { return }
self.packetScoreReports["sleep"] = sleepReport
self.refreshPrimarySleepFromScoreReport()
self.packetScoreReports["strain"] = strainReport
self.packetScoreReports["recovery"] = recoveryReport
self.packetScoreReports["stress"] = stressReport
self.packetScoreStatus = "Bridge packet-derived scores recomputed"
}
} catch {
let shortErr = Self.shortError(error)
DispatchQueue.main.async { [weak self] in
guard let self else { return }
self.packetScoreStatus = "Bridge score run blocked: \(shortErr)"
}
}
}
}

func runSleepScore() {
do {
packetScoreReports["sleep"] = try sleepScoreReport(baseArgs: bridgeBaseArgs(requireTrustedEvidence: false))
refreshPrimarySleepFromScoreReport()
packetScoreStatus = "Bridge sleep score recomputed"
} catch {
packetScoreStatus = "Bridge sleep score blocked: \(Self.shortError(error))"
packetScoreStatus = "Extracting bridge sleep score..."
let baseArgs = bridgeBaseArgs(requireTrustedEvidence: false)
let bridge = self.bridge
packetInputQueue.async { [weak self] in
do {
let sleepReport = try bridge.request(
method: "metrics.sleep_score_from_features",
args: baseArgs.merging([
"sleep_need_minutes": 480.0,
"low_motion_threshold_0_to_1": 0.05,
"disturbance_motion_threshold_0_to_1": 0.20,
"target_midpoint_minutes_since_midnight": 180.0,
"history_import_in_progress": false,
"algorithm_id": "goose.sleep.v1",
]) { _, new in new }
)
DispatchQueue.main.async { [weak self] in
guard let self else { return }
self.packetScoreReports["sleep"] = sleepReport
self.refreshPrimarySleepFromScoreReport()
self.packetScoreStatus = "Bridge sleep score recomputed"
}
} catch {
let shortErr = Self.shortError(error)
DispatchQueue.main.async { [weak self] in
guard let self else { return }
self.packetScoreStatus = "Bridge sleep score blocked: \(shortErr)"
}
}
}
}

Expand Down
69 changes: 40 additions & 29 deletions GooseSwift/HealthDataStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,38 +162,49 @@ final class HealthDataStore: ObservableObject {
}

func refreshBridgeCatalogs() {
do {
let algorithmsValue = try bridge.requestValue(method: "metrics.built_in_definitions")
let referencesValue = try bridge.requestValue(method: "metrics.reference_definitions")
let preferencesValue = try bridge.requestValue(method: "metrics.default_preferences")
catalogStatus = "Loading bridge catalog..."
let bridge = self.bridge
packetInputQueue.async { [weak self] in
do {
let algorithmsValue = try bridge.requestValue(method: "metrics.built_in_definitions")
let referencesValue = try bridge.requestValue(method: "metrics.reference_definitions")
let preferencesValue = try bridge.requestValue(method: "metrics.default_preferences")

let parsedAlgorithms = Self.algorithmRows(from: algorithmsValue)
.map { HealthAlgorithmDefinition(row: $0, source: .bridge("metrics.built_in_definitions")) }
let parsedReferences = Self.algorithmRows(from: referencesValue)
.map { HealthAlgorithmDefinition(row: $0, source: .bridge("metrics.reference_definitions")) }
let parsedPreferences = Self.preferenceRows(from: preferencesValue)
let parsedAlgorithms = Self.algorithmRows(from: algorithmsValue)
.map { HealthAlgorithmDefinition(row: $0, source: .bridge("metrics.built_in_definitions")) }
let parsedReferences = Self.algorithmRows(from: referencesValue)
.map { HealthAlgorithmDefinition(row: $0, source: .bridge("metrics.reference_definitions")) }
let parsedPreferences = Self.preferenceRows(from: preferencesValue)

if !parsedAlgorithms.isEmpty {
algorithmDefinitions = parsedAlgorithms
}
if !parsedReferences.isEmpty {
referenceDefinitions = parsedReferences
}
if !parsedPreferences.isEmpty {
selectedAlgorithmByFamily = parsedPreferences
} else {
selectedAlgorithmByFamily = Dictionary(
uniqueKeysWithValues: algorithmDefinitions.map { ($0.family, $0.id) }
)
DispatchQueue.main.async { [weak self] in
guard let self else { return }
if !parsedAlgorithms.isEmpty {
self.algorithmDefinitions = parsedAlgorithms
}
if !parsedReferences.isEmpty {
self.referenceDefinitions = parsedReferences
}
if !parsedPreferences.isEmpty {
self.selectedAlgorithmByFamily = parsedPreferences
} else {
self.selectedAlgorithmByFamily = Dictionary(
uniqueKeysWithValues: self.algorithmDefinitions.map { ($0.family, $0.id) }
)
}
self.catalogSource = .bridge("Rust metric registry")
self.catalogStatus = "Bridge catalog loaded"
}
} catch {
let shortErr = Self.shortError(error)
DispatchQueue.main.async { [weak self] in
guard let self else { return }
self.algorithmDefinitions = []
self.referenceDefinitions = []
self.selectedAlgorithmByFamily = [:]
self.catalogSource = .unavailable("Rust catalog unavailable")
self.catalogStatus = "Metric catalog unavailable: \(shortErr)"
}
}
catalogSource = .bridge("Rust metric registry")
catalogStatus = "Bridge catalog loaded"
} catch {
algorithmDefinitions = []
referenceDefinitions = []
selectedAlgorithmByFamily = [:]
catalogSource = .unavailable("Rust catalog unavailable")
catalogStatus = "Metric catalog unavailable: \(Self.shortError(error))"
}
}

Expand Down
21 changes: 15 additions & 6 deletions Rust/core/src/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ struct ParseFrameBatchArgs {
frames: Vec<String>,
#[serde(default = "default_device_type")]
device_type: String,
#[serde(default = "default_true")]
include_result: bool,
}

#[derive(Debug, Clone, Deserialize)]
Expand Down Expand Up @@ -2546,12 +2548,19 @@ fn parse_frame_hex_batch_bridge(args: ParseFrameBatchArgs) -> GooseResult<serde_
let mut results = Vec::with_capacity(args.frames.len());
for (index, frame_hex) in args.frames.iter().enumerate() {
match parse_frame_hex(device_type, frame_hex) {
Ok(parsed) => results.push(json!({
"index": index,
"ok": true,
"compact": compact_parsed_frame_summary(&parsed),
"result": parsed,
})),
Ok(parsed) => {
let mut item = json!({
"index": index,
"ok": true,
"compact": compact_parsed_frame_summary(&parsed),
});
if args.include_result {
if let Some(obj) = item.as_object_mut() {
obj.insert("result".to_string(), json!(parsed));
}
}
results.push(item);
}
Err(error) => results.push(json!({
"index": index,
"ok": false,
Expand Down