From a6503fbddacacf714d6a1d0f3da384a228e35de0 Mon Sep 17 00:00:00 2001 From: Fanboynz Date: Fri, 28 Aug 2026 18:09:56 +1200 Subject: [PATCH 1/2] ci: stop path-filtering the Tools suites, since one of them asserts on the whole repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The workflow's own header said it was "path-filtered so it only fires when the suites it runs change". That premise is false, and it cost a red main. test_home_i18n.py asserts on the CONTENTS of app and package files. #1671 moved a sign and a hardcoded °C out of a localized literal in AppModel.swift and broke it. The commit touched only Swift, the filter is Tools/*.py, so this job never ran - main sat red until @Zebsi235 touched Tools/ for an unrelated Windows path fix and tripped over it in their own PR (#1691), where it read as a failure they had caused. Widening the filter instead would re-create the trap one edit later. The Tools suites currently assert on 33 paths outside Tools/, across Strand/, StrandiOS/, NOOPWatch/, Packages/ and android/ - AppModel.swift, four xcstrings catalogues, ReadinessEngine on both platforms, TodayScreen.kt, strings.xml, an optical oracle fixture. That list is whatever the assertions happen to read today, and a filter that has to track it is a filter that will be wrong again. The job is about 25 seconds on ubuntu with no dependencies and no fixtures to fetch. Running it on every PR is cheaper than a gate that is green because it did not run - which is the same failure this repo just spent a day finding in three other places. Verified locally: Tools 108 tests 0 failures, Tools/linux-capture 212 tests 0 failures, and the workflow still parses with pull_request, push and workflow_dispatch triggers. --- .github/workflows/tools-python.yml | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tools-python.yml b/.github/workflows/tools-python.yml index fc18f4429a..f0910c8e16 100644 --- a/.github/workflows/tools-python.yml +++ b/.github/workflows/tools-python.yml @@ -5,23 +5,26 @@ # one of them only ever executed when a contributor remembered to, which meant a decoder could regress and # the suite that would have caught it stayed green by never being run. # -# Cheap to run: pure stdlib `unittest`, no dependencies, no fixtures to fetch, ubuntu rather than macOS. -# Path-filtered so it only fires when the suites it runs change. +# Cheap to run: pure stdlib `unittest`, no dependencies, no fixtures to fetch, ubuntu rather than macOS — +# about 25 seconds end to end. +# +# NOT path-filtered, and that is deliberate. It used to fire only on `Tools/**.py`, on the premise that a +# suite can only break when its own source changes. That is false for `test_home_i18n.py`, which asserts +# on the CONTENTS of app and package files — AppModel.swift, the xcstrings catalogues, ReadinessEngine, +# DayNavBar. A Swift-only commit broke it (#1671 moved a sign and a hardcoded °C out of a localized +# literal) and the filter meant this job never ran, so `main` sat red until a contributor touching +# `Tools/` for something unrelated tripped over it in their own PR (#1691). +# +# Widening the filter to name those paths would just re-create the trap one edit later: the list is +# whatever the assertions happen to read today. Twenty-five seconds on every PR is cheaper than a gate +# that is green because it did not run. name: Tools Python CI on: pull_request: branches: [main] - paths: - - 'Tools/linux-capture/**' - - 'Tools/*.py' - - '.github/workflows/tools-python.yml' push: branches: [main] - paths: - - 'Tools/linux-capture/**' - - 'Tools/*.py' - - '.github/workflows/tools-python.yml' workflow_dispatch: concurrency: From e7a4c07229cf52d7cf0490b8c309d2fb15e5076f Mon Sep 17 00:00:00 2001 From: Fanboynz Date: Fri, 28 Aug 2026 18:13:47 +1200 Subject: [PATCH 2/2] ci: the twin-drift guards never ran when the other platform changed Re-review of the Tools trigger fix generalised to the other two gates, and the result is sharper than the original bug. This repo's core parity mechanism is a set of tests that deliberately read the OTHER platform's fixture to catch a one-sided edit. DecoderOracleTest on Android opens Packages/WhoopProtocol/Tests/.../decoder_oracle.json and asserts the two committed copies decode identically; the Swift package tests open the android/ copies for the same reason. Its own comment says why: "Because the two decoders are independent reimplementations of the same byte layout, decoding the same fixture and asserting the same output is what catches a one-sided edit." Neither workflow fired on the edit it was written to catch. android.yml filtered to android/** but its tests read 4 paths outside it swift-packages.yml filtered to Packages/** + 3 Tools but its tests read 4 paths outside it So editing only the Swift oracle skipped the Android guard, and editing only the Android oracle skipped the Swift one. The tests existed, passed, and were structurally unable to run at the moment they mattered. Both filters now name the cross-boundary fixtures: the protocol schema, the decoder and schema oracles on each side, Strand's xcstrings catalogue, and the one package test that reads Strand/Liquid/LiquidCore.swift. Targeted rather than removed, unlike the Tools job in the previous commit, and the difference is cost: Tools is 25 seconds and reads 33 paths across the whole repo, so a filter there is both expensive to maintain and pointless. android.yml is minutes and reads four specific files. Naming four files is proportionate; running a six-minute Android build on every Swift comment change is not. The comment in each says the list has to track the assertions, which is the honest weakness of the targeted form. Verified: both files parse, android.yml goes 2 -> 6 paths on both triggers, swift-packages 5 -> 7. --- .github/workflows/android.yml | 16 ++++++++++++++++ .github/workflows/swift-packages.yml | 10 ++++++++++ 2 files changed, 26 insertions(+) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index e650d7af6a..467e4759b8 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -12,11 +12,27 @@ on: paths: - 'android/**' - '.github/workflows/android.yml' + # The twin-drift guards read the SWIFT side's fixtures on purpose — DecoderOracleTest compares + # against Packages/.../decoder_oracle.json to catch a one-sided edit, and the schema/protocol + # oracles work the same way. Without these, editing the Swift copy alone leaves the very test + # written to catch that from ever running. Keep this list in step with what the tests read. + - 'Packages/WhoopProtocol/Sources/WhoopProtocol/Resources/**' + - 'Packages/WhoopProtocol/Tests/WhoopProtocolTests/Resources/**' + - 'Packages/WhoopStore/Tests/WhoopStoreTests/Resources/**' + - 'Strand/Resources/Localizable.xcstrings' push: branches: [main] paths: - 'android/**' - '.github/workflows/android.yml' + # The twin-drift guards read the SWIFT side's fixtures on purpose — DecoderOracleTest compares + # against Packages/.../decoder_oracle.json to catch a one-sided edit, and the schema/protocol + # oracles work the same way. Without these, editing the Swift copy alone leaves the very test + # written to catch that from ever running. Keep this list in step with what the tests read. + - 'Packages/WhoopProtocol/Sources/WhoopProtocol/Resources/**' + - 'Packages/WhoopProtocol/Tests/WhoopProtocolTests/Resources/**' + - 'Packages/WhoopStore/Tests/WhoopStoreTests/Resources/**' + - 'Strand/Resources/Localizable.xcstrings' workflow_dispatch: concurrency: diff --git a/.github/workflows/swift-packages.yml b/.github/workflows/swift-packages.yml index c46bb25628..cb46672dcf 100644 --- a/.github/workflows/swift-packages.yml +++ b/.github/workflows/swift-packages.yml @@ -16,6 +16,11 @@ on: - 'Tools/SleepPSG/**' - 'Tools/Backfill/**' - '.github/workflows/swift-packages.yml' + # Mirror of the note in android.yml: package tests read the ANDROID oracles to catch a one-sided + # edit, and one reads Strand/Liquid/LiquidCore.swift. Editing only that side would otherwise skip + # the guard written for exactly that case. Keep in step with what the tests read. + - 'android/app/src/test/resources/**' + - 'Strand/Liquid/LiquidCore.swift' push: branches: [main] paths: @@ -24,6 +29,11 @@ on: - 'Tools/SleepPSG/**' - 'Tools/Backfill/**' - '.github/workflows/swift-packages.yml' + # Mirror of the note in android.yml: package tests read the ANDROID oracles to catch a one-sided + # edit, and one reads Strand/Liquid/LiquidCore.swift. Editing only that side would otherwise skip + # the guard written for exactly that case. Keep in step with what the tests read. + - 'android/app/src/test/resources/**' + - 'Strand/Liquid/LiquidCore.swift' workflow_dispatch: concurrency: