Commit Graph

4 Commits

Author SHA1 Message Date
Matt Toohey
fae3f0346c test(mobile): keep late callbacks and fixed sleeps from failing tests (#8678)
## Summary

Fixes three mobile flakes from the 21–28 Oct 2026 CI runs, in test code
only:

- In two iOS `RunnerTests` classes, late bridge callbacks no longer fail
whichever test is running when they fire.
- The waits on a busy test-host main thread now cap at 10 s.
- Two Flutter avatar tests poll for the ready frame instead of sleeping
a fixed 50 ms.

This is one of seven PRs split out of the `flakey-tests` branch
(siblings below).

### Related issue

None found.

### Flaky tests fixed

| Test | Failing runs | Cause | Change |
|---|---|---|---|
| `BuzzPushSnapshotEnrichmentTests` (RunnerTests) | 40362671989 (job
121712747802, main), 39681273328#1 (job 119504560518), 40658588384 (job
122715950541, PR #8640) | The 1 s waits ran out while the test host's
Flutter app held the main thread, and the test returned and deleted its
temp directory. The bridge callback's assertions then ran later and were
blamed on whichever test was running (`ProfileSetupRecoveryTests`,
`IdentityFilePluginTests`). | Callbacks only record the value and
fulfill. Every assertion, including the `isRestricted` checks, runs
after the wait, so a late callback writes into a dead local. The seven
waits cap at 10 s (`callbackTimeout`). |
| `ProfileSetupRecoveryTests`
`testStartedEditorWithoutReadyRestoresProfileAndAllowsRetry` |
40585805357 (job 122477274956, PR #8568) | Engine release and editor
dismissal must both happen within 2 s, on the main thread the host app
keeps busy. | Both predicate waits cap at 10 s. |
| Flutter `channel_detail_page_test.dart` "profile sheet shows the
poster before autoplay and restores it on tap" and
`settings_profile_header_test.dart` "shows the poster until the animated
avatar is ready" | 40368315739 (job 121732711851, main) | After the
image response is released, both tests slept a fixed 50 ms of real time
before `pumpAndSettle`. A slower real decode left no frame scheduled, so
the ready key was missing. | A new `WidgetHelpers.pumpUntilFound` pumps
in 10 ms real-time steps until the ready key appears, capped at about 5
s. |

Local evidence is from the original `flakey-tests` commit (53aaec97c),
Xcode 27.1 on the iOS 26.2 simulator:

- Enrichment, with `Self.complete` delayed 1.5 s and 4 s: 6 failures
before, 107/107 after. With a 12 s delay, every failure lands in its
owning test. A mutated error code is still caught.
- Profile setup, with the engine release held 4 s: 2 failures before,
4/4 after. A leaked engine still fails.
- Avatar tests: under load, 94/96 before and 96/96 after. With a 200 ms
image delay, 0/5 before and 5/5 after. A widget that never signals ready
still fails.

Not included:

- The planned `AppDelegate` / `Runner.xcscheme` change that keeps
Flutter out of the RunnerTests host. It did not work locally: restored
scene sessions and the `UIMainStoryboardFile` fallback, and a normal
launch came up black afterwards.
- `IdentityFilePluginTests`
`testSyntheticDocumentsReadInPlaceAndRejectInvalidOrOversize`
(40441255307#1). It is left unchanged and stays open.

### Verification

This branch was pushed without local re-verification, by the user's
choice: local test runs were overloading the machine and failing for
that reason. **Hosted CI is the check.** It was pushed with `git push
--no-verify`, because the pre-push hook runs the Flutter tests.

- Every file is byte-identical to its final version on `flakey-tests`
(67185df6a).
- Checked for this PR: `git diff --check`, and a conflict-free merge
with all six siblings onto `main`.
- #8613's flake-check lane will repeat the two changed Dart test files.

### Readiness

Test-only change. AGENTS.md's readiness steps (agent review, then human
confirmation) are still outstanding, so `buzz-review-completed` is not
set.

### Merge order

Independent of the other six. It merges cleanly onto `main`, and with
every sibling, in any order.

### Sibling PRs

Split from `flakey-tests` (one PR per area): #8676 (PostgreSQL lease +
lane), #8677 (relay E2E helper + Windows shell), #8679 (Tauri apt cache
+ Windows Rust cap), #8680 (desktop product races), #8681 (desktop E2E
state gates), #8682 (desktop E2E composer + row actions).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Jitter <2b2e6415e748c35180847a37aadae06a11e30dddc76b784e7fd9354c4eb42e7a@buzz.block.builderlab.xyz>
2026-10-30 11:58:40 +11:00
Matt Toohey
b1f76ef4d2 ci: cache the Tauri apt .debs and fit a cold Windows Rust run in its cap (#8679)
## Summary

Fixes the two CI workflow defects that turned `main` red most often in
the 21–28 Oct 2026 audit:

- **Desktop Tauri:** the lane now caches its apt `.deb`s. It no longer
re-downloads 62 MB from a slow Ubuntu mirror on every run.
- **Windows Rust:** the lane drops a duplicate `cargo check` and gets a
60 min cap, so a cold run can save its cache.

It also updates #8613's `scripts/flake-check.test.mjs`, which breaks
once the Tauri package list moves into an env var. There are no product
code or test-behaviour changes.

This is one of seven PRs split out of the `flakey-tests` branch
(siblings below).

### Related issue

None found. #8613 touched both workflows after the audit, but not these
steps. No open PR changes how the Tauri packages are fetched, the
Windows timeout, or its Check (workspace) step:

- #8422, #8321 and #8373 only add packages to the list, and would now
add them to `TAURI_APT_PACKAGES`.
- #8414 adds two mesh-llm Tauri steps to the Windows job. If it lands,
they must fit the cold budget below.

### CI failures fixed

| Lane / test | Failing runs | Cause | Change |
|---|---|---|---|
| Desktop Tauri, "Install Tauri dependencies (Linux)"
(`_ci-desktop.yml`) | 62 jobs in 51 run attempts, 22–27 Oct. On main:
39821769576, 39822883395, 39827234118, 40333705511, 40342468627,
40348069929, 40353619362, 40368315739, 40373404267, 40374519539,
40556742929 | All 3 `ci-apt-retry.sh` attempts hit their 420 s limit
while `azure.archive.ubuntu.com` served the 62 MB at 20–65 KB/s. The
25.5 MB `libwebkit2gtk-4.1-0` alone takes a whole attempt, and each
attempt loses its partial download. | The package list moves to a
job-level `TAURI_APT_PACKAGES`. A new "Resolve Tauri apt archives" step
builds the cache key from `apt-get install --print-uris -o
Acquire::ForceHash=SHA256` (name, size and SHA256 per file).
`actions/cache/restore` uses that key. The install step copies only the
restored files whose SHA256 matches the signed index, because apt 2.8.3
reuses any same-size cached `.deb` without hashing it. A best-effort
rebuild keeps exactly the listed files. `actions/cache/save` runs only
on core-lane pushes after a successful rebuild; PR runs never save. |
| Windows Rust (`_ci-rust.yml`) | All on main: 39796569342 (job
119885795086), 39821769576 (119975358772), 39827234118 (119994286695),
39865872891 (120128351939), 40402556064 (121852634685) | Every test
passed. Cold steps take 41.3–43.5 min, so the 45 min cap cancelled the
`Swatinem/rust-cache` post-run save 1.6–3.7 min in. No cache was
written, and the next push was cold again. | Delete "Check (workspace)".
The clippy step before it covers the same `--workspace --all-targets`
set with `-D warnings`, which saves 2.0–2.8 min. Raise `timeout-minutes`
from 45 to 60, with the measured breakdown in a comment: 39–41 min of
steps, a ~4.5 min save and ~15 min of margin. |
| `scripts/flake-check.test.mjs` "repeat invocations mirror the lanes
they stand in for" | None in CI yet: it fails only with this change
applied | #8613 added this test after `flakey-tests` branched. It checks
that the advisory `flake-tauri` lane installs the same packages as the
Desktop Tauri core lane, by regex-matching the inline `apt-get install
-y --no-install-recommends \` list in `_ci-desktop.yml`. That list now
lives in `TAURI_APT_PACKAGES`, so the regex matched nothing and the test
failed with "Desktop Tauri apt packages". | Read `TAURI_APT_PACKAGES`
from `_ci-desktop.yml` and compare package names (12 on each side) with
`_ci-flake.yml`'s list. Renaming one package in `_ci-flake.yml` still
fails the test. Giving the `flake-tauri` lane the same read-only `.deb`
restore is a follow-up. |

Local evidence is from the original `flakey-tests` commit (9aa768a3b):

- The apt steps ran verbatim in Ubuntu 24.04 containers with apt 2.8.3,
with the cache restore and the save step's `if:` emulated.
- On a miss, the install fetches everything ("Need to get 254 MB"). On
an exact hit with the network down, it fetches nothing ("Need to get 0
B/254 MB").
- On a fallback with one `.deb` missing and one corrupted at the same
size, the hash check rejects the corrupt file, and apt re-fetches only
the corrupt and missing files.
- The key was identical in all four simulated jobs.
- The Windows numbers come from the jobs API for the five runs above.

On this branch, the split session ran the `scripts/` node tests:
`ci-selection`, `ci-required-gates`, `ci-apt-retry` and `flake-check` on
macOS gave 84 pass, 0 fail and 8 skipped (Linux-only). Before the
`flake-check.test.mjs` fix, that test failed 1 case. The same session
ran actionlint 1.7.12 with shellcheck, which was clean, and the
rust-cache contract scripts, which passed.

### Verification

This branch was pushed without further local re-verification, by the
user's choice: local test runs were overloading the machine and failing
for that reason. **Hosted CI is the check.** It was pushed with `git
push --no-verify` over SSH. The pre-push hook runs tests, and the HTTPS
token lacks the `workflow` scope.

Only hosted CI can show:

- The runner's real package list (about 62 MB) and the same key in all
three Tauri lanes.
- The `actions/cache` round trip. The first PR run misses, because
`main` has no key yet. After merge, the first push to `main` saves it.
- A cold Windows run saving its cache within the new cap. The next
toolchain bump or cache eviction will show it.

Remaining risk: after Ubuntu updates a large package, runs fetch it from
the mirror until a push to `main` saves a new key.

### Readiness

CI-only change. AGENTS.md's readiness steps (agent review, then human
confirmation) are still outstanding, so `buzz-review-completed` is not
set.

### Merge order

Independent of the other six. It merges cleanly onto `main`, and with
every sibling, in any order.

### Sibling PRs

Split from `flakey-tests` (one PR per area): #8676 (PostgreSQL lease +
lane), #8677 (relay E2E helper + Windows shell), #8678 (mobile), #8680
(desktop product races), #8681 (desktop E2E state gates), #8682 (desktop
E2E composer + row actions).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 10:00:00 +11:00
klopez4212
39b8b2b6a6 feat(mobile): unify Messages navigation with Solo and Team filters (#8649)
## Summary

Unify mobile conversations in Messages with All / Solo / Team filters, a
collapsing header, and separate Search, New, and default-agent
shortcuts. Move workspace actions into the community menu and keep
collapsed sections intact across filters.

Align Inbox and list typography, spacing, and muted controls. Preserve
native glass layering through navigation and give every filtered list
enough clearance above the footer. The default-agent shortcut reads the
shared encrypted preference and opens an existing chat immediately. When
unset, it opens Agents; a star action selects the default through a
production signed, self-encrypted preference writer. Save failures
remain visible and retryable.

Android now uses rounded navigation controls and filters, optically
balanced search icons, and expanding New/community menus with leading
Tabler icons matching iOS. Preset avatars retain transparent WebM
animation through an Android WebView renderer, with lifecycle pausing
and poster fallback.

### Related issue

None found in searches for mobile navigation issues and open PRs.

### Testing

- Android follow-up: ARM64 debug build, Dart analysis, and emulator
checks passed for menu placement, selection, icon alignment, search, and
avatar playback/pause/resume. The avatar implementation passed 3,840
Flutter tests and native Android tests before subsequent styling
iterations. Automated tests for the later menu/icon iterations were
skipped at the author’s request; those earlier results do not cover the
final styling changes.

- Mobile formatting/analysis and full test suite (3,838 passed, 23
skipped, plus 4 push-configuration tests); regression journey covers
filtering, collapse retention, and scrolling the last Solo row above the
footer.
- iPhone 18 Pro simulator (Honeycomb): filters, menus, Inbox,
Invite/Back, Switch Community, and default-agent chat/Back exercised.
iOS release build and Pairing Preview install/launch passed during
iteration, preserving the saved profile.
- P1 follow-up: production writer-to-reader widget journey covers
selection, publish failure/retry, and the unset footer opening Agents.
Simulator Agents open/close passes; its saved community has no
selectable agents, so live selection was not verified there.
- Independent agent review completed; its Solo footer-clearance finding
is fixed. The final clearance adjustment is covered by widget tests;
physical-device acceptance of that adjustment and hosted CI remain
pending.

Final Messages layout:

![Messages with All, Solo and Team filters and separate bottom
shortcuts](https://github.com/user-attachments/assets/562ddc3e-945b-4945-bd58-217a2c62b570)

Switcher layering during iteration (before → after):

| Before: filter edge showing through | After: Home hidden behind
switcher |
| --- | --- |
| ![Filter edge visible beside Switch
Community](https://github.com/user-attachments/assets/b32a0157-8b17-4f01-9a23-29b12ce3b1af)
| ![Clean Switch Community
screen](https://github.com/user-attachments/assets/222956e4-9989-4c8f-a162-65de45029845)
|

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
2026-10-29 14:27:22 +00:00
Matt Toohey
eda26b9423 ci: repeat changed tests ten times on pull requests (#8613)
This ports the advisory flake check from block/buzz-app
(https://github.com/block/buzz-app/pull/890) to this repository: pull
requests repeat the tests they change ten times, with retries off, so a
test that passes CI by luck is caught before it merges.

## What it does

`flake-select` diffs the PR merge commit against its first parent
(`HEAD^1..HEAD`, `fetch-depth: 2`) and maps changed hunks to tests per
runner: a hunk inside a test body selects that test; a change anywhere
else in a test file, or a new file, selects the whole file. One lane per
runner then repeats only that selection 10× with retries disabled and
writes a test × iteration table to its step summary. Lanes skip when
nothing was selected, run only on `pull_request`, and are not read by
any required gate in `ci.yml` — their own statuses carry the result.

## Per runner

| Runner | Selection granularity | Repeat mechanism |
|---|---|---|
| Rust (cargo-nextest) | `crates/<crate>/<path>.rs::<module::test>` from
`#[test]`-style attribute spans (comments and literals blanked first). A
hunk outside any test body selects the file's tests. `#[ignore]`d and
Postgres / `external_infra` tests are excluded. | One `cargo nextest run
--profile flake` per package per iteration, with target flags derived
from the path (`src/**` → `--lib --bins`, `tests/<stem>.rs` → `--test
<stem>`), a filterset of exact `binary_id & test` pairs, and JUnit
parsed per iteration. New `[profile.flake]`: `retries = 0`, `fail-fast =
false`. |
| node:test | Whole changed `desktop/src/**/*.test.mjs`,
`*.jsdom-test.mjs`, `desktop/scripts/*.test.mjs` | 10× loop, each file
alone, with the flag sets from `desktop/package.json` `test` |
| Playwright `smoke` | `file:line` journeys from `test()`/`it()` spans
(TypeScript AST) intersected with `playwright test --list
--project=smoke`, deduplicated | Native `--repeat-each=10 --retries=0`,
sharded `min(10, ceil(journeys / 5))` ways; `playwright-summary`
tabulates the JSON report |
| Flutter | Whole changed `mobile/test/**/*_test.dart` | 10× `flutter
test --file-reporter=json` loop with the mobile lane's `--dart-define` |

A runner whose selection exceeds 64 KiB of entries, or more than 250
Playwright journeys, is skipped with a note rather than overrunning its
timeout.

## Differences from the buzz-app version

- Lanes live in a reusable `_ci-flake.yml` called from `ci.yml`,
matching this repo's `_ci-*.yml` convention, instead of inline jobs.
- Playwright specs are parsed with the desktop app's own `typescript`
package rather than `rolldown/parseAst`.
- No Playwright `integration` project lane: it needs the relay artifact
chain. Integration-only specs list nothing for `smoke` and the selection
stays empty.
- Playwright shards are capped at 10, the same bound as the regular
smoke lane.
- Rust entries carry the file and in-file module path, and the filterset
is anchored to exact `(binary, test)` pairs, so a same-named test in
another module is not repeated.
- nextest target flags are derived from the changed file's path, so a
`buzz-relay` change does not compile every test target of the crate.
- A journey-count cap (250) skips the Playwright lane for selections ten
repetitions could not finish within its 45-minute timeout.

## Heads-up

- The Unit Tests `rust-cache` moves from `key: sherpa-cache-v1` to
`shared-key: unit-tests-sherpa-cache-v1` so the flake lane can restore
it read-only. This changes the cache key once: the first Unit Tests run
after merge builds cold.
- This PR itself changes no file the selector recognises
(`scripts/flake-check.test.mjs` is a root script, run by the Desktop
lane), so its own Flake Check lanes are expected to skip.

## Verification

- `node --test scripts/flake-check.test.mjs` — all unit tests pass (also
wired into the Desktop lane).
- `actionlint` on the changed workflows; `biome check` on the two
scripts.
- `scripts/test-rust-cache-contract.sh` and its regression harness pass
with the new shared key.
- Synthetic-diff selection runs for each runner, plus `select` against
this PR's own merge-base (empty, as expected).
- 2-iteration repeats of a real selection on all four runners locally.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-29 14:34:20 +11:00