fix(mobile): stop clipping expanded tool groups#10212

merged1
t3code/mobile-subagent-spawn-batches
t3code/mobile-work-group-expand-sizing
openedSep 5, 2026byJJulius Marminge
View on GitHubTwo-way sync enabledPR last imported 3d ago

Stacked on #10211.

Problem

Opening a row's details inside an expanded tool group left an unpainted band the size of the growth (88 points for a five-line command output). Layout was correct everywhere — LegendList's sizes, the outer row's measured height, and accessibility frames all matched — but the pixels for everything past the group's old bottom edge were missing until the group was collapsed and reopened.

The group's scroll-edge fades come from @expo/ui's MaskedView, which hosts the list inside a SwiftUI .mask via RNHostView. That host keeps the bounds it had when the mask was built, so a resize clips the tail. #9359 worked around the first symptom by forcing the list's height; this is the second.

Confirmed by elimination on the simulator: removing layout animations, the inner ScrollView, selectable, absoluteFill, and maintainVisibleContentPosition each left the band in place; swapping MaskedView for a plain clipped View removed it.

Fix

  • Render the group in a plain overflow: hidden view and draw the fades as two screen-colored SVG gradients over the edges, each fading in only while content continues past that edge (same scroll-offset math, inverted from "cover" to "fade"). The screen color comes from ThreadFeed, which already reads the theme.
  • Read the group's content size from LegendList's totalSize signal instead of the native onContentSizeChange, so the outer row resizes in the same commit as the row measurement that changed it.

ShimmeringWorkContent still uses MaskedView; its mask never resizes.

Verification

  • tsc --noEmit for apps/mobile, targeted lint and format.
  • iPhone 17 Pro simulator, same seeded group on main and this branch. The blank band after expanding the ffmpeg row: 88 points → 0. Fades verified on a 12-call group: bottom fade present at offset 0, top fade present when scrolled to the end, both painted in the screen color.
  • 14 consecutive expand/collapse cycles across home → thread → group → row with the process pinned: no crash.

Before: an 88-point blank band under the expanded row. After: the rows below paint in place.

Recording of the expand/collapse, before on the left:

https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/f0dfbe332a6d42b8/stack-pair-expand.mp4

Implemented by Claude Fable 5 in the Claude Code harness.

Note

Fix clipped expanded tool groups and settle workflow coordinator members in thread activity

  • Replaces the MaskedView composite mask in ThreadWorkGroupList with a clipped container and separate animated EdgeFade overlays that fade only at edges with remaining content, using the current screen theme color
  • Propagates the Uniwind theme screen color through ThreadFeed and ThreadWorkLog so edge fades match the background
  • Updates isAgentInternalActivity to retain task lifecycle rows with a task identifier and agent kind for batch processing instead of filtering them as internal
  • In agentSpawnRow, a terminal workflow coordinator now settles members that lack their own end event or remain in progress; explicitly reported member statuses are preserved
  • Maps idle task status to completed in toDerivedWorkLogEntry when no other lifecycle status is present
  • Adds tests for bypassed workflow members and idle Codex children folding into coordinator batch rows
  • Risk: ThreadWorkGroupList now uses a ref callback subscribing to LegendList total-size updates instead of the native content-size callback; verify scroll fade visibility and content-height bookkeeping remain correct under rapid resize and theme switches

Macroscope summarized 65b1b9c.

Synced from GitHub

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ No successful main baseline artifact is available yet. This run establishes the initial measurement.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire—13.6 KiB—15.1 KiB✅
CodexThread snapshot wire—7.0 KiB—7.3 KiB✅
CodexLive turn WebSocket wire—6.6 KiB—7.8 KiB✅
CodexLive turn WebSocket decoded—57.1 KiB—66.4 KiB✅
CodexLive turn messages—10—21✅
ClaudeTotal thread wire—13.5 KiB—15.1 KiB✅
ClaudeThread snapshot wire—7.0 KiB—7.3 KiB✅
ClaudeLive turn WebSocket wire—6.5 KiB—7.8 KiB✅
ClaudeLive turn WebSocket decoded—57.8 KiB—66.4 KiB✅
ClaudeLive turn messages—9—21✅

Baseline: unavailable · PR result: 65b1b9c · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 113.8 KiB
  • Claude decoded thread snapshot: 114.5 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Synced from GitHub

Approvability

Verdict: Approved at 5e25d25

Macroscope's review found this PR approvable — This is a contained mobile UI bug fix that replaces the problematic masked group rendering with a clipped list and localized edge fades, while synchronizing expanded-group sizing with LegendList measurements. Its effects are limited to existing expanded tool groups and do not introduce new capabilities, schema changes, product-default changes, or static-analysis overrides.

No code changes detected at 65b1b9c. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

Added 1 commit

  • fix(mobile): stop clipping expanded tool groups65b1b9c