From 5e43ab599335f4f7b3cb3f9748263f3dae79c2bb Mon Sep 17 00:00:00 2001 From: AidenJohnston Date: Sat, 26 Sep 2026 19:18:44 -0500 Subject: [PATCH 1/4] ci(ios): drop the background Simulator boot On the 3-core runner it slowed swift test from 1:22 to 5:25 and the device build from 1:00 to 1:54, costing about 6 minutes to save 2:40. The first run of #57 took 19:21; the audit skip alone should give about 15. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/ios.yml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index c48739365..189c56d19 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -65,9 +65,9 @@ jobs: xcodebuild -version swift --version - # Booting takes 2 to 3 minutes and needs nothing from the build, so it runs alongside the - # steps below; the UI test step waits for it. - - name: Boot the Simulator in the background + # Booting in the background was tried and dropped: on the runner's 3 cores it slowed + # swift test and the builds by about 6 minutes to save 2:40. xcodebuild boots it at test time. + - name: Find the Simulator run: | runtime="com.apple.CoreSimulator.SimRuntime.iOS-${SIM_OS//./-}" udid="$(xcrun simctl list devices available -j \ @@ -79,7 +79,6 @@ jobs: exit 1 fi echo "SIM_UDID=$udid" >> "$GITHUB_ENV" - nohup xcrun simctl bootstatus "$udid" -b > "$RUNNER_TEMP/simulator-boot.log" 2>&1 & # The package's drift tests compare the vendored schemas with the server's and the capture # packet's own copies. Before those branches merge they exist only as branches, which the @@ -125,12 +124,6 @@ jobs: xcodebuild -project ios/HouseScan.xcodeproj -scheme HouseScan -configuration Debug -destination "generic/platform=iOS" CODE_SIGNING_ALLOWED=NO COMPILER_INDEX_STORE_ENABLE=NO build - - name: Wait for the Simulator - timeout-minutes: 10 - run: | - xcrun simctl bootstatus "$SIM_UDID" -b - echo "Booted $SIM_NAME on iOS $SIM_OS ($SIM_UDID)." - - name: UI test the whole flow in the Simulator # Replays the committed synthetic wall (ios/HouseScanUITests/Fixtures/synthetic-wall) with # -autopilot and the bundled sample result. From f765a6066e16e7684c773cabfebf909066ee4b78 Mon Sep 17 00:00:00 2001 From: AidenJohnston Date: Sat, 26 Sep 2026 20:02:15 -0500 Subject: [PATCH 2/4] ci(ios): stop the duplicate run on pushes to t3/ios-mvf PR #10 (t3/ios-mvf into main) already tests every merge into t3/ios-mvf, so the push run took a second macOS slot for the same code. PR #10 gets the full-ui label so the accessibility audit still runs once per merge. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/ios.yml | 4 +++- CONTRIBUTING.md | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index 189c56d19..b8a17110c 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -1,8 +1,10 @@ name: iOS on: + # No push trigger for t3/ios-mvf: PR #10 (t3/ios-mvf -> main) already tests every merge into + # it, and carries full-ui so the audit runs there. A push run would take a second macOS slot. push: - branches: [main, t3/ios-mvf] + branches: [main] paths: - "ios/**" - "!ios/**/*.md" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 20f705eb8..1db0996bf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,7 +32,7 @@ The `size:*` label describes the effective diff. It is a review signal, not a me | Sync label definitions | `.github/labels.json` changes on `main` | none | | Label PR size | a pull request opens or updates | none | -The iOS UI tests skip the every-state accessibility audit on pull requests, because it adds about 10 minutes and macOS runners are scarce. Add the `full-ui` label when a pull request changes screens or copy; pushes to `t3/ios-mvf` and `main` always run it. +The iOS UI tests skip the every-state accessibility audit on pull requests, because it adds about 10 minutes and macOS runners are scarce. Add the `full-ui` label when a pull request changes screens or copy; pushes to `main` always run it, and PR #10 (`t3/ios-mvf` into `main`) carries the label, so every merge into `t3/ios-mvf` runs it once. `make check` runs the three local suites. They need uv, Node 24 with pnpm, and Xcode 26 or newer; each directory's README has details. No check is required by branch rules yet. Don't call one required until the rules require its status. From e0ba7266818afc7f16a5da0054f729e2a9584518 Mon Sep 17 00:00:00 2001 From: AidenJohnston Date: Sat, 26 Sep 2026 20:07:25 -0500 Subject: [PATCH 3/4] ci(ios): fetch t3/server when server/ has no schemas The contract fetch skipped t3/server whenever a server/ folder existed. t3/ios-mvf has one from the toolchain setup with no schemas in it, so the fetch never ran and vendoredCopiesMatchServer failed every iOS run under HOUSESCAN_REQUIRE_UPSTREAM. Check for a file the tests read instead. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/ios.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index 6caad576e..b45e947c0 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -89,14 +89,16 @@ jobs: # than skip, so CI can't go green without comparing. - name: Fetch the contract branches run: | - for pair in server:t3/server packet:t3/packet; do - folder="${pair%%:*}" branch="${pair#*:}" - if [ -d "$folder" ]; then - echo "$folder/ is in the checkout; the drift tests read it." + # Check for a file the tests read, not the folder: t3/ios-mvf has a server/ from the + # toolchain setup with no schemas in it. + for pair in server/schemas/scene.schema.json:t3/server packet/manifest.schema.json:t3/packet; do + file="${pair%%:*}" branch="${pair#*:}" + if [ -f "$file" ]; then + echo "$file is in the checkout; the drift tests read it." continue fi if ! git fetch --no-tags --depth=1 origin "+refs/heads/$branch:refs/remotes/origin/$branch"; then - echo "::error::Neither $folder/ nor $branch is available, so the vendored $folder schemas can't be compared." + echo "::error::Neither $file nor $branch is available, so the vendored ${file%%/*} schemas can't be compared." exit 1 fi done From 3c1298ab46869730418c5cafc6b4d60eafe0e6fb Mon Sep 17 00:00:00 2001 From: AidenJohnston Date: Sat, 26 Sep 2026 20:27:46 -0500 Subject: [PATCH 4/4] test(ios): delete the packet manifest drift test t3/packet removed packet/manifest.schema.json in 6a12700 when the team adopted the server's capture packet 0.4, so the test compared the vendored copy with a file that no longer exists and failed every iOS run under HOUSESCAN_REQUIRE_UPSTREAM. Deleted with Sam's OK. CI stops fetching t3/packet, which nothing reads now. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/ios.yml | 11 +++++------ .../HouseScanKitTests/PacketWriterTests.swift | 16 ++-------------- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index b45e947c0..aaf84e113 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -82,16 +82,15 @@ jobs: fi echo "SIM_UDID=$udid" >> "$GITHUB_ENV" - # The package's drift tests compare the vendored schemas with the server's and the capture - # packet's own copies: the checkout's server/ and packet/ once merged, else the t3/server and - # t3/packet branches, which the shallow checkout leaves out. Either source is required here: - # a failed fetch fails the run, and HOUSESCAN_REQUIRE_UPSTREAM makes the tests fail rather - # than skip, so CI can't go green without comparing. + # The package's drift tests compare the vendored scene and result schemas with the server's: + # the checkout's server/schemas once merged, else the t3/server branch, which the shallow + # checkout leaves out. One of the two is required: a failed fetch fails the run, and + # HOUSESCAN_REQUIRE_UPSTREAM makes the tests fail rather than skip. - name: Fetch the contract branches run: | # Check for a file the tests read, not the folder: t3/ios-mvf has a server/ from the # toolchain setup with no schemas in it. - for pair in server/schemas/scene.schema.json:t3/server packet/manifest.schema.json:t3/packet; do + for pair in server/schemas/scene.schema.json:t3/server; do file="${pair%%:*}" branch="${pair#*:}" if [ -f "$file" ]; then echo "$file is in the checkout; the drift tests read it." diff --git a/ios/HouseScanKit/Tests/HouseScanKitTests/PacketWriterTests.swift b/ios/HouseScanKit/Tests/HouseScanKitTests/PacketWriterTests.swift index 18a887ad9..7429adfae 100644 --- a/ios/HouseScanKit/Tests/HouseScanKitTests/PacketWriterTests.swift +++ b/ios/HouseScanKit/Tests/HouseScanKitTests/PacketWriterTests.swift @@ -8,12 +8,10 @@ import Testing /// The capture packet's manifest schema, version 1.1, byte for byte from t3/packet at d5439cf /// (packet/manifest.schema.json). `vendoredManifestSchemaIsTheRecordedRevision` fails if the copy -/// is edited by hand; `vendoredManifestSchemaMatchesThePacketTree` compares it with the repo's -/// packet/ folder once that branch is merged, and with origin/t3/packet through git until then. +/// is edited by hand. t3/packet no longer carries the schema (6a12700: the team adopted the +/// server's capture packet 0.4), so there is no upstream copy to compare against. enum PacketSchema { static let name = "manifest.schema.json" - static let repoPath = "packet/manifest.schema.json" - static let branch = "origin/t3/packet" static let sha256 = "44c9c9beb1a95b85d069d9abc52c193008a50aa75e1dff52f5d47431f10c5c75" static func validator() throws -> JSONSchemaValidator { try JSONSchemaValidator(schema: SceneSchemas.data(name)) } @@ -801,16 +799,6 @@ private func photoJPEG() throws -> URL { #expect(digest == PacketSchema.sha256, "Schemas/\(PacketSchema.name) is not the copy taken from t3/packet d5439cf") } - /// Skipped, not passed, where neither packet/ nor origin/t3/packet can be read, except under - /// `SceneSchemas.requireUpstream` (CI), where that fails. - @Test(.enabled( - if: SceneSchemas.requireUpstream || SceneSchemas.upstream(PacketSchema.repoPath, branch: PacketSchema.branch) != nil, - "neither packet/ nor \(PacketSchema.branch) is available to compare against")) - func vendoredManifestSchemaMatchesThePacketTree() throws { - let upstream = try #require(SceneSchemas.upstream(PacketSchema.repoPath, branch: PacketSchema.branch)) - #expect(upstream == (try SceneSchemas.data(PacketSchema.name)), "copy \(PacketSchema.repoPath) over Tests/HouseScanKitTests/Schemas/\(PacketSchema.name)") - } - /// The schema is not a rubber stamp: a manifest missing required fields or breaking a pattern /// fails. @Test func schemaRejectsBrokenManifests() throws {