Skip to content

fix(limrun): list the tabs of a collapsed iOS tab bar in snapshots - #4019

Open
thymikee wants to merge 6 commits into
mainfrom
maint/3992-limrun-ios-tab-bar-probes
Open

thymikee wants to merge 6 commits into
mainfrom
maint/3992-limrun-ios-tab-bar-probes

Conversation

@thymikee

@thymikee thymikee commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Summary

On a Limrun iPhone, a native UITabBar arrives from elementTree() as one childless Group "Tab Bar", so snapshot had no tabs. The adapter now hit-tests across it with elementTree(point) and publishes the tabs as its children, with labels, frames and selected state.

  • Candidates: childless, wide Groups docked along the bottom edge, ranked by tab-bar height (44–100 pt), then lowest top edge.
  • Probes: at most 24 per snapshot, shared evenly, at least one per 44 pt, in parallel under a 1.5 s timeout.
  • Tabs: TabButton hits passing the runner's rule; geometry shared via contracts/fixtures/ios-collapsed-tab-geometry.json.
  • Failure: all or nothing. The bar stays as reported with a typed residue, collapsed-tab-bar-probe-failed (rejected, timed-out, budget-exhausted), shown as a warning. Exhaustion is reported only for tab-bar-sized candidates.

iPhone scope: a bar wider than 1,056 pt reports budget-exhausted before any probe. #3967 edits the same call; on rebase, probes use its source option.

Fixes #3992

Validation

  • Regressions fail on base, pass at head; 1323 provider-limrun and capture-kit tests passed.
  • Live at 5e8c71d (this head's code), Limrun iPhone 16 Pro, iOS 26.4, test app, 5 runs: 13 concurrent probes, batch 155–157 ms, each 108–157 ms (~10× under 1.5 s). snapshot --json lists Home*, Catalog, Form, Audio, Settings under Tab Bar, no tab-bar warning, 925–991 ms. press label="Catalog" then shows Catalog selected.
  • Swift golden test compiles locally; the macOS host lane runs it.
  • check:affected --run --head fa03636: passed, exactCommit: true.

🤖 Generated with Claude Code

A Limrun iPhone reports a native UITabBar as one childless Group along
the viewport's bottom edge, so snapshots had no tabs to press. Limrun's
elementTree(point) hit-test does return the tab buttons. The snapshot
adapter now probes a bounded row of points across such a bar, in
parallel, and publishes the distinct TabButton hits as its children,
applying the Apple runner's collapsed tab container rule.

Fixes #3992

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
agent-device-website Ignored Ignored Preview Oct 28, 2026 3:05pm UTC

Request Review

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 5 files

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread packages/provider-limrun/src/ios-snapshot-adapter.fixtures.ts
Comment thread packages/provider-limrun/src/ios-collapsed-tab-bar.ts Outdated
@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://callstack.github.io/agent-device/pr-preview/pr-4019/

Built to branch gh-pages at 2026-10-28 15:23 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Size Report

Metric Base Current Diff
Installed (including dependencies) 5.56 MB 5.56 MB +3.7 kB
Package (unpacked) 5.56 MB 5.56 MB +3.7 kB
Package (download) 1.69 MB 1.69 MB +1.4 kB

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 33.3 ms 32.4 ms -0.9 ms
CLI --help 64.1 ms 62.2 ms -1.8 ms

Review round 1 on #4019.

- A candidate whose hit-tests find no tabs no longer hides a later one.
  One 24-probe budget spans every candidate; each gets at least one
  probe per 44 pt.
- Expansion is all or nothing. A rejected hit-test, or one slower than
  1.5 s, keeps that bar as reported and adds a typed
  `collapsed-tab-bar-probe-failed` residue, which the snapshot presents
  as a warning. A partial tab set would look complete.
- The container, candidate and row geometry now lives in
  contracts/fixtures/ios-collapsed-tab-geometry.json, read by both the
  Limrun tests and the runner's XCTest; the runner's row check moves to
  `collapsedTabRowFits` for that.
- The fixture carries all five live test-app tabs; a landscape case is
  added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 13 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread packages/provider-limrun/src/ios-collapsed-tab-bar.test.ts
Comment thread packages/provider-limrun/src/ios-collapsed-tab-bar.ts Outdated
Review round 1 on #4019, second Cubic pass, plus the R73 layering fix.

- Candidates are probed most tab-bar-like first: a tab bar's height
  (44-100 pt), then the lowest top edge. The tree's label is not used.
- Each candidate gets an even share of the probes left, raised to one
  probe per 44 pt, so a decoy cannot spend the whole budget.
- When the budget runs out before a candidate is confirmed, the
  snapshot carries the same typed residue with reason
  `budget-exhausted`.
- The provider now reports `collapsedTabBarProbeFailure` and capture-kit
  derives the residue, as R73 requires.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 7 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread packages/capture-kit/src/ios-snapshot-acquisition.ts Outdated
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@thymikee

Copy link
Copy Markdown
Member Author

The code needs one live check before merge. At 4558c85 the 1.5 s probe timeout could leave real Limrun iPhone snapshots without the tabs. CI is green (21 checks, none failing), and there are no conflicts.

The only live run in the PR body is at b4651d6. That commit had no 1.5 s race, no candidate ranking and no budget sharing. The head fires 13 elementTree(point) requests at once in ios-collapsed-tab-bar.ts and gives up after 1.5 s on the whole batch. The reported 152 ms is a per-probe median. If the Limrun device server handles hit-tests one after another, 13 x ~150 ms is about 2 s, which is past the timeout. Every snapshot could then return an unexpanded bar plus a warning, which is the #3992 symptom again. No unit test can show this. Please run snapshot --json once at the head on a Limrun iPhone (iOS 26.x) against an app with a native UITabBar. It must list the tabs as children with labels and selected state, and its warnings must not contain collapsed-tab-bar-probe-failed. Please also report the wall time of the probe batch. Then press a tab by label and confirm the next snapshot shows it selected. If the batch time is near 1.5 s, raise the timeout or cap concurrency.

Not blocking, take or leave: a childless Group of at least 160 pt docked along the sides and bottom is a candidate whatever its height, so a budget-exhausted result can warn "retry the snapshot" when no tab bar exists (for example a wide bottom strip on iPad). You could report budget exhaustion only for candidates with a tab-bar height (44-100 pt), or drop "Retry the snapshot" from that wording. Also, each snapshot with such a Group sends about 13 hit-tests and there is no cache, so reporting the added latency in the live run would help.

Of the other review threads, the five from cubic-dev-ai (tab fixtures, rejection handling, probe midline test, budget sharing and the doc comment) are fixed at this head, so they can be resolved. No other thread still applies.

I could not tell whether Limrun's device server runs concurrent hit-tests in parallel, and I did not run the TS or Swift tests locally. The next step before merge is the live run above at 4558c85.

…ates

A wide bottom strip outside the tab-bar height range, such as a home
indicator area on iPad, no longer adds a budget-exhausted residue when
no tab bar exists. The budget-exhausted warning no longer suggests a
retry, which cannot change a deterministic result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 5 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread packages/contracts/src/ios-snapshot.ts Outdated
Comment thread packages/provider-limrun/src/ios-collapsed-tab-bar.ts Outdated
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@thymikee

Copy link
Copy Markdown
Member Author

Live check done at 5e8c71d. fa03636 on top of it changes comments only.

Setup: Limrun iPhone 16 Pro, iOS 26.4 (402×874), the test app's Release build with a native UITabBar, five runs. A timing wrapper sat around each elementTree(point), while the head's own captureLimrunIosSnapshot did the work.

Probe batch:

  • Each snapshot sends 13 probes.
  • The batch takes 155–157 ms wall time, and each probe takes 108–157 ms.
  • The probes overlap almost completely, so the device server answers hit-tests concurrently, not one after another.
  • The full capture takes about 735 ms after the first run (1162 ms cold).
  • The 1.5 s bound therefore has about 10× headroom, so I left it unchanged. The timeout test already pins that a hung probe settles within 2 s.
  • For comparison, the earlier raw-SDK run took 1385 ms for 12 sequential probes. The 152 ms in the PR body was already the batch wall time, not a per-probe median.

CLI at the head, same instance, attached through LIM_IOS_INSTANCE_URL/TOKEN:

  • snapshot --json ×5 lists Home (selected), Catalog, Form, Audio and Settings as children of Tab Bar.
  • The only warnings are the three standard Limrun ones (hittability, depth, truncation). None mentions the collapsed tab bar.
  • Wall time is 925–991 ms per snapshot.
  • press 'label="Catalog"' tapped (131, 822), and the next snapshot shows Catalog selected and Home unselected.

The instance is deleted.

Your take-or-leave note: applied in 5e8c71d. Budget exhaustion is now reported only when an unreached candidate has a tab-bar height (44–100 pt), so a 20 pt bottom strip 1194 pt wide gives no warning. A 50 pt bar of the same width still reports one. The budget-exhausted warning no longer says "Retry the snapshot", because the result is deterministic; rejected and timed-out keep it. Both cases have tests, and the first one failed before the fix. On the cost: a snapshot with a candidate pays about 155 ms for the probe batch, and there's no cache, because tabs and their selection change between captures.

Gate: check:affected --run --head fa03636 passed, exactCommit: true.

@thymikee

Copy link
Copy Markdown
Member Author

The code looks good at fa03636. The earlier review at 4558c85 asked for live evidence and doc fixes. The comment fixes are in, and the live Limrun run is now reported at 5e8c71d (batch wall time 155-157 ms, tabs listed, press by label). I could not reproduce that run without a Limrun instance, and I did not run the TS tests or check:affected locally. I read the earlier code to confirm the new strip test would have failed before the change. CI is green: 21 checks, 0 not passing. There are no conflicts.

Not blocking, take or leave: the warning suffix in packages/capture-kit/src/snapshot/ios-snapshot-runtime.ts#L111 now depends on entry.reason (no "Retry the snapshot." for budget-exhausted), but no test asserts the presented warning text, so inverting the condition keeps every test green. A check of the full presented warning through presentIosSnapshotAcquisition for one budget-exhausted case and one timed-out or rejected case would cover it.

Both P3 threads from cubic-dev-ai are fixed at this commit, so please resolve them: contracts/src/ios-snapshot.ts now lists rejected, timed-out and budget-exhausted with their conditions (#4019 (comment)), and provider-limrun/src/ios-collapsed-tab-bar.ts now names each probeFailure value and its condition (#4019 (comment)).

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Oct 28, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Limrun iOS: the iOS 26 tab bar is one empty node, its tabs are missing from the snapshot

1 participant