From 5c7fbb21d4a471634baa8b9655ea79a920c87c3e Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 17 Jun 2026 16:04:29 +0000 Subject: [PATCH 1/3] fix(desktop): clamp Goal.progress to its documented 0-100 range current_value below min_value produced a negative percentage that leaked into the goal progress ring (Path.trim) and the "% complete" prompt text. Callers only clamped the upper bound, so clamp at the source instead. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01K6oBTTKdSzfrvKNg1yXnuB --- desktop/macos/Desktop/Sources/APIClient.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/desktop/macos/Desktop/Sources/APIClient.swift b/desktop/macos/Desktop/Sources/APIClient.swift index ccc9e05fa1..105cb68317 100644 --- a/desktop/macos/Desktop/Sources/APIClient.swift +++ b/desktop/macos/Desktop/Sources/APIClient.swift @@ -2727,7 +2727,8 @@ struct Goal: Codable, Identifiable { /// Progress as a percentage (0-100), based on targetValue var progress: Double { guard targetValue != minValue else { return 0 } - return ((currentValue - minValue) / (targetValue - minValue)) * 100.0 + let pct = ((currentValue - minValue) / (targetValue - minValue)) * 100.0 + return min(max(pct, 0), 100) } /// Whether the goal is completed From 7df19508cb361db9380136ad6a27ffd05d2051d0 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 17 Jun 2026 16:04:39 +0000 Subject: [PATCH 2/3] test(desktop): cover Goal.progress 0-100 clamping Adds GoalProgressTests: mid-range, below-min clamps to 0 (the regression), overachievement clamps to 100, and the target==min divide-by-zero guard. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01K6oBTTKdSzfrvKNg1yXnuB --- .../Desktop/Tests/GoalProgressTests.swift | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 desktop/macos/Desktop/Tests/GoalProgressTests.swift diff --git a/desktop/macos/Desktop/Tests/GoalProgressTests.swift b/desktop/macos/Desktop/Tests/GoalProgressTests.swift new file mode 100644 index 0000000000..c888574e90 --- /dev/null +++ b/desktop/macos/Desktop/Tests/GoalProgressTests.swift @@ -0,0 +1,48 @@ +import XCTest + +@testable import Omi_Computer + +/// Unit tests for `Goal.progress`, which must stay within its documented +/// 0-100 range. Regression coverage for a bug where `current_value` below +/// `min_value` produced a negative percentage that leaked into the goal +/// progress ring (`Path.trim`) and the "% complete" prompt text. +final class GoalProgressTests: XCTestCase { + + /// Decode a `Goal` from minimal JSON. Dates are omitted so they fall back + /// to the decoder defaults — `progress` does not depend on them. + private func makeGoal(min: Double, target: Double, current: Double) throws -> Goal { + let json = """ + { + "id": "g1", + "goal_type": "numeric", + "min_value": \(min), + "target_value": \(target), + "current_value": \(current) + } + """ + return try JSONDecoder().decode(Goal.self, from: Data(json.utf8)) + } + + func testProgressMidRange() throws { + let goal = try makeGoal(min: 0, target: 10, current: 5) + XCTAssertEqual(goal.progress, 50, accuracy: 0.0001) + } + + func testProgressClampsNegativeToZero() throws { + // current below min would yield (2-5)/(15-5)*100 = -30 without clamping. + let goal = try makeGoal(min: 5, target: 15, current: 2) + XCTAssertEqual(goal.progress, 0, accuracy: 0.0001, "Progress must not go below 0") + } + + func testProgressClampsOverachievementToHundred() throws { + // current above target would yield 200 without clamping. + let goal = try makeGoal(min: 0, target: 10, current: 20) + XCTAssertEqual(goal.progress, 100, accuracy: 0.0001, "Progress must not exceed 100") + } + + func testProgressIsZeroWhenTargetEqualsMin() throws { + // Guard against divide-by-zero when the range is degenerate. + let goal = try makeGoal(min: 5, target: 5, current: 5) + XCTAssertEqual(goal.progress, 0, accuracy: 0.0001) + } +} From ec9eb416dd6dbfe0a3f0348e8d39072dee03627c Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 17 Jun 2026 16:04:39 +0000 Subject: [PATCH 3/3] chore(desktop): changelog entry for goal progress clamp Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01K6oBTTKdSzfrvKNg1yXnuB --- desktop/macos/CHANGELOG.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/desktop/macos/CHANGELOG.json b/desktop/macos/CHANGELOG.json index 5b296cd8d9..1f16a0b8d1 100644 --- a/desktop/macos/CHANGELOG.json +++ b/desktop/macos/CHANGELOG.json @@ -1,5 +1,7 @@ { - "unreleased": [], + "unreleased": [ + "Fixed goal progress rings and labels showing an incorrect value when a tracked value dropped below its starting point" + ], "releases": [ { "version": "0.11.467",