Files
orca/mobile/src/tasks
Jinwoo Hong 335d111265 fix(mobile): name the Back control in every page-served screen (OTA phase C, C2.8) (#21715)
* fix(mobile): name the Back control in every page-served screen

The C2.7 device proof could only reach the tasks Back by tapping the
native control's coordinates: inside the shell there is no native chrome
behind it, so a bare Pressable is absent from the accessibility tree and
a screen reader has nothing to announce. The agent-history panel's Back
carries a label but no role; the tasks one carries neither.

Both gain `accessibilityRole="button"`, and the tasks one the app's own
wording, `accessibilityLabel="Back"` — the spelling its thirteen
siblings use, including the agent-history panel this screen sits beside.
The host screen's Back already carried both and did not move.

The new census is a rule rather than a count: it finds every Back
affordance under the screen tree each page route serves — a Pressable
rendering a ChevronLeft that either carries the back-button style or
calls back — and requires a button role and a Back-shaped label on each.
Its coverage assertion reads MOBILE_WEB_PAGE_ROUTES, so a route
registered without a screen entry fails here, the way the flag census
fails for a switched route. Red first on both offenders by name.

Two tasks parity hashes move, and only the two a JSX prop must move:
`semantics` 3,272 -> 3,274 for the widened host signature and the two
new runtime strings, and the render-token stream 35,195 -> 35,203 for
the eight tokens the two attributes are. Hooks, statements,
declarations and styles hold, which is the evidence that nothing
executable changed. Recorded in the test's comment.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* fix(mobile): judge a Back control by what it does, and name the files pair

Round 1 found the census discriminating on the ChevronLeft glyph, which
separates nothing: dismisses sit in the same header slot with the same
back-button style, so the rule claimed a dismiss and told it to be
called Back, while the explorer's Close escaped only by drawing an X.

The predicate is now the behaviour. A Back control is a Pressable whose
press handler reaches a back call — a `back()`, `goBack()` or `onBack()`
call, a handler named for one, or a local handler whose declaration
makes one — or whose label already says Back. A control matching
neither is outside the rule whatever it renders. The reviewer's
mutation, a dismiss with the back-button style wrapping a ChevronLeft
behind `onDismiss`, is claimed by the old predicate and passed over by
this one, which is the whole of the difference.

A spread now reads as unknown rather than absent, so a Pressable that
hides these props behind one is a Back control that fails both rules
instead of a control the scan never sees. No Pressable in the five
trees spreads today, so the branch is inert and load-bearing.

C3.1 registered the files explorer and preview, so the coverage
assertion went red on its merge, as designed. Both screens join
PAGE_SERVED_SCREENS and both Back controls gain
`accessibilityRole="button"`; they already carried `Back to session`
and `Back to files`. The tasks Back gains the `hitSlop={8}` its four
siblings carry, so its touch target is no longer the glyph alone.

Nine mutations: the dismiss stays out; a dropped role on each of the
five controls and a dropped label on the tasks one go red by name; a
spread fails both rules; a dropped table entry fails coverage; and
dropping the host screen's label — its only detector, since
`actions.leaveHost` is opaque — empties that tree and fails the
presence assertion rather than passing in silence.

Two parity hashes move for the one added prop and no count does:
`semantics` holds at 3,274 lines with one changed, the host signature
gaining `hitSlop`, and the render-token stream gains that attribute's
four tokens, 35,203 -> 35,207. Recorded in the test's comment.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* fix(mobile): assert a Back per page route, not per screen directory

Round 2 found the presence assertion reading directories while two
routes share `src/files`, so the explorer's Back answered for the
preview's as well: rewriting the preview's control to `onPress=
{handleClose}` with `accessibilityLabel="Close"` left that route with
no accessible Back and the suite green. Reproduced exactly, then made
red by this change, which names the route and the module it failed in.

Each PAGE_SERVED_SCREENS entry now names the module that renders its
route's Back, and presence is asserted per entry. The rules still read
whole trees, so a Back added beside a screen is ruled as well as the
screen's own; the tree is derived from the module's directory rather
than declared twice. The failure prints the offending list instead of
comparing two values a reader cannot tell apart.

The doc comment claimed the preview was carried by the label half of
the predicate. It is not: `requestBack` is named for what it does, so
the preview is found behaviourally like the explorer, the tasks screen
and the agent-history panel. Dropping the label from each of those
three leaves it detected and failing the naming rule, which is the
evidence. Only the host screen depends on the label half, because
`actions.leaveHost` is a member access this rule does not follow.

The admitted residual gap stays admitted and the predicate is not
widened for it: a second Back control in a screen that already has one,
with an opaque handler and a label that does not say Back, is still
outside what this rule sees.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-09-19 16:36:21 -04:00
..
2026-05-30 11:05:58 -07:00