Skip to content

Stop the app tests asserting the host's unified memory - #1

Merged
rexmhall09 merged 1 commit into
mainfrom
fix/ci-device-capabilities
Aug 30, 2026
Merged

Stop the app tests asserting the host's unified memory#1
rexmhall09 merged 1 commit into
mainfrom
fix/ci-device-capabilities

Conversation

@rexmhall09

Copy link
Copy Markdown
Owner

Why CI is red

The head commit only touched README and site assets. The app test suites were gating on the RAM of whatever machine ran them.

AppModel.init defaults deviceCapabilities to .current(), which reads hw.memsize. That feeds the eligibility gate behind canLoadModel, canReloadModel, canInstallModel, and canInstallVisionPack. GitHub's macos-26-arm64 runners have 7 GB — under the 8 GB catalog floor for the 26B — so every install, load, and companion action was refused before the state machine under test ever ran.

ConversationHistoryTests then indexed a conversation the load had never filled, trapped with Fatal error: Index out of range, and killed the test process — which is why the symlink, Markdown-link, and app-version steps never ran. The 2026-08-27 run failed the same way at a different point.

The change

None of these tests need the hardware. The installer and inference clients are mocks, and makeCompleteModelInstall writes zero-byte weight files — nothing downloads, loads, or allocates a working set.

  • New Tests/TUFFApp/Core/Support/AppModelTestHost.swift: a makeAppModel(...) factory mirroring AppModel.init argument for argument, pinning a qualifying device. All 143 AppModel( sites in Tests/TUFFApp/Core route through it. This follows the pattern already in AppWorkspaceSmokeTests.
  • #require(...first) replaces the two trapping index accesses, so one failure reports instead of taking the run down.

No production code changes — deliberately no env-var or runtime override, per the CONTRIBUTING guardrail against undocumented runtime switches.

Device eligibility keeps its real coverage in ModelCatalogTests, which injects 8 GiB and expects refusal (including the exact "Requires 16 GB unified memory; this Mac has 8 GB." text) and injects 16 GiB and expects the opposite. Those now state their device deliberately rather than inheriting it from 143 sites.

Verification

  • current() temporarily patched to the runner's exact 7 GB: 1376 tests / 231 suites pass (patch reverted; Sources/ is untouched in this PR)
  • Full suite on real hardware: 1376 tests / 231 suites pass
  • check_tracked_symlinks.rb, check_markdown_links.rb, check_app_version.rb all pass
  • Did not run swift build -c release — it already succeeded on CI for this commit and no Sources/ file changed

Known limit

The pinned 32 GiB fixture won't notice a future catalog floor raised above it. A 7 GB runner wouldn't catch that either — it refuses everything regardless. RecommendedMemoryTests and ModelCatalogTests are where that belongs. Nothing yet stops a new test from calling AppModel( directly and reintroducing host dependence; a Scripts/check_*.rb guard would fit the repo's idiom if wanted.

🤖 Generated with Claude Code

`AppModel.init` defaults `deviceCapabilities` to `.current()`, which reads
`hw.memsize`. That flows into the eligibility gate behind `canLoadModel`,
`canReloadModel`, `canInstallModel`, and `canInstallVisionPack`, so 143 test
call sites were asserting the RAM of whatever machine ran the suite.

The Apple-silicon CI runners have 7 GB, under the 8 GB catalog floor for the
26B. Every install, load, and companion action was refused before the state
machine under test ever ran, failing eight suites for a reason unrelated to
the code under test. `ConversationHistoryTests` then indexed the conversation
the load had never filled and trapped, killing the process and hiding the
three CI steps after it.

None of these tests need the hardware: the installer and inference clients are
mocks and `makeCompleteModelInstall` writes zero-byte weights. Route them
through `makeAppModel`, which mirrors the initializer and pins a qualifying
device. Hardware gating keeps its coverage in `ModelCatalogTests`, where each
test states the device it means, and `#require` replaces the two index
accesses so one failure no longer takes the run down with it.

Verified with `current()` patched to the runner's 7 GB: 1376 tests pass.
@rexmhall09
rexmhall09 merged commit c6deb30 into main Aug 30, 2026
1 check failed
@rexmhall09
rexmhall09 deleted the fix/ci-device-capabilities branch August 30, 2026 09:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant