# Proof audit, pass 1

Findings first

1. **Visual proof is missing.** No capture exists for any required row F01–F08 below. This is a fixture derivation, not a runtime verification or a general merge-readiness review. The changed behavior is list membership after a non-empty trimmed query; its appearance/disappearance, keyboard behavior, list transitions, and save/cancel behavior require recordings. A still of a populated list cannot establish those transitions.
2. **Claude catalog claim: verified, with a mobile eligibility qualification.** The bundled `claudeAgent` catalog contains `claude-opus-4-8`, label `Claude Opus 4.8`, `status: legacy`, minimum Claude CLI version `2.1.154`. No provider credential is required to load that bundled catalog into a server snapshot. However, mobile `buildModelOptions` excludes a disabled/project-disabled provider, a missing CLI, or `auth.status === "unauthenticated"`. A fresh database by itself does not guarantee a selectable Claude catalog on mobile. `unknown` auth is admitted. Do not claim that a signed-out healthy CLI necessarily works, or capture the transient pending snapshot: pending Claude reports `installed: false` and is excluded.
3. **The shared picker has three callers.** The affected render path is reached by a new-task draft, an existing thread (also a queued-message edit), and Settings → Scheduled tasks → Model. The last caller must not be mistaken for an unrelated picker. The wrappers need no Cartesian duplication of every fixture; allocate F02 to new-task, F04 to existing-thread, and F08 to scheduled-task to cover all three while reusing the same core fixture data.
4. **Hidden exclusion is upstream, not in the new helper.** Normal `buildModelOptions` removes user-hidden bundled models before grouping, including the hidden selected-model fallback. Manage hidden models rebuilds groups with `includeHidden: true` and passes `showLegacy: true`; showing hidden entries there is intentional. The eye control and Hidden marker must be distinguished from the Legacy badge.
5. **No implementation change is requested.** Read-only inspection found no contradiction to the specified search behavior. This pass does not certify CI, live provider eligibility, permission availability, or visual quality.

Scope and source

Reviewed [#27587](https://git.cafe/pingdotgg/t3code/pull/27587), branch `fix/issue-27573`, pinned head [03ec49bf](https://github.com/pingdotgg/t3code/commit/03ec49bf7f1b7722a59db840a5ecb27c3fa94655), using the supplied comparison base [32ecd717](https://github.com/pingdotgg/t3code/commit/32ecd71726fee16f335484c1f95cbce126d461f6). Issue: [#27573](https://github.com/pingdotgg/t3code/issues/27573). Ran the requested `git -C /home/ubuntu/wt/pr-27587 diff 32ecd71726fee16f335484c1f95cbce126d461f6...HEAD`; verified HEAD matches. The local moving `main` ref has a different merge-base; the supplied pinned comparison, not that moving ref, defines this report. Only two production files and the adjacent state test differ. No worktree edits, pushes, comments, servers, simulators, or browsers were started.

Primary source locations at the reviewed revision:

- [ThreadSettingsSheet.tsx](https://github.com/pingdotgg/t3code/blob/03ec49bf7f1b7722a59db840a5ecb27c3fa94655/apps/mobile/src/features/threads/ThreadSettingsSheet.tsx): session/settings at lines 325–639; list building 736–807; options 810–908; list 910–1063; filters/search/header 1184–1403; wrappers 1470–1579.
- [thread-settings-sheet-state.ts](https://github.com/pingdotgg/t3code/blob/03ec49bf7f1b7722a59db840a5ecb27c3fa94655/apps/mobile/src/features/threads/thread-settings-sheet-state.ts): query fields/normalization, legacy exception, Favorites, stable favorites-first partition, provider collapse, selection/commit guards. This module renders no UI and has only one production caller of `visibleCatalogModels`, the list builder above.
- [modelOptions.ts](https://github.com/pingdotgg/t3code/blob/03ec49bf7f1b7722a59db840a5ecb27c3fa94655/apps/mobile/src/lib/modelOptions.ts#L239): provider eligibility, locks, grouping, hidden models, fallback selection.
- [bundled manifest](https://github.com/pingdotgg/t3code/blob/03ec49bf7f1b7722a59db840a5ecb27c3fa94655/apps/server/src/provider/model-manifest.json#L734), [ClaudeModelCatalog.ts](https://github.com/pingdotgg/t3code/blob/03ec49bf7f1b7722a59db840a5ecb27c3fa94655/apps/server/src/provider/ClaudeModelCatalog.ts#L59), [ClaudeProvider.ts](https://github.com/pingdotgg/t3code/blob/03ec49bf7f1b7722a59db840a5ecb27c3fa94655/apps/server/src/provider/ClaudeProvider.ts#L453), and [catalogServerModel](https://github.com/pingdotgg/t3code/blob/03ec49bf7f1b7722a59db840a5ecb27c3fa94655/packages/provider-core/src/server/ModelCatalog.ts#L40): credential-independent bundled data, legacy classification, installation/auth/version qualifications.
- [ScheduledTaskPickerScreens.tsx](https://github.com/pingdotgg/t3code/blob/03ec49bf7f1b7722a59db840a5ecb27c3fa94655/apps/mobile/src/features/settings/ScheduledTaskPickerScreens.tsx#L23), [ThreadComposer.tsx](https://github.com/pingdotgg/t3code/blob/03ec49bf7f1b7722a59db840a5ecb27c3fa94655/apps/mobile/src/features/threads/ThreadComposer.tsx#L417), [Stack.tsx](https://github.com/pingdotgg/t3code/blob/03ec49bf7f1b7722a59db840a5ecb27c3fa94655/apps/mobile/src/Stack.tsx#L788): entry points and presentations.
- [shared row rendering](https://github.com/pingdotgg/t3code/blob/03ec49bf7f1b7722a59db840a5ecb27c3fa94655/apps/mobile/src/features/threads/ThreadSettingsRows.shared.tsx), [iOS rows](https://github.com/pingdotgg/t3code/blob/03ec49bf7f1b7722a59db840a5ecb27c3fa94655/apps/mobile/src/features/threads/ThreadSettingsRows.tsx), [Android rows](https://github.com/pingdotgg/t3code/blob/03ec49bf7f1b7722a59db840a5ecb27c3fa94655/apps/mobile/src/features/threads/ThreadSettingsRows.android.tsx).

Fixture matrix

`P` = one permitted real pooled device: iPhone 17 Pro with the Expo dev client, native portrait viewport, default text scale, light theme. Record actual OS/build and viewport dimensions instead of assuming pixel dimensions. Pixel 9 portrait may replace P consistently if iOS is unavailable. `K` = show both focused search with keyboard and the settled, keyboard-dismissed result where applicable. All F rows are required diff proof (changed output or a direct acceptance boundary); S rows inventory unchanged visual branches and are outside the minimum. No S row is claimed captured or passed.

| ID / named fixture | Diff relation | Required data and visible state | Seed / real-app path | Evidence and minimum viewport × theme | Pass-1 status |
| --- | --- | --- | --- | --- | --- |
| F01 — default and whitespace reset | Boundary: still collapsed | Current Claude selection; unhidden, unfavorited Opus 4.8; Show legacy off; Manage hidden off; All providers. Empty query omits target. Enter three spaces: same model membership and section-collapse behavior; Android clear icon may appear because it checks raw length. Clear again. | Base B below; new-task picker. Keep target neither selected nor favorite. | **recording**, P × light × F01, K. Can share F02 clip. | MISSING |
| F02 — exact single legacy hit | **Changed**: formerly absent | F01 data. Type `Claude Opus 4.8`: one matching target, Legacy badge, unfilled star, rounded single-row group, no selection check. Repeat `  cLaUdE oPuS 4.8  ` and `claude-opus-4-8` to exercise normalization and slug match. Clear: disappears again. | B; composer model control → `NewTaskThreadSettings` (`new/draft/settings`) after opening a draft. No agent turn. | **recording**, P × light × F02, K; paired base/head recording or clearly labelled before/after segments using identical seed. | MISSING |
| F03 — broad query with mixed current/legacy and ordering | **Changed**: multi-row list expands | At least one current and two legacy Claude models; one current favorite, at least one unstarred legacy; another provider group. Query `Claude` matches provider label as well as names, reveals legacy rows, keeps favorites first, and forces included headers expanded/non-collapsible. Clear restores default provider collapse; exercise first/middle/last row borders and scroll to the last hit/options. | B; favorite a current model; All providers. Use a collapsed secondary group before search if available; otherwise its collapse branch is S04. | **recording**, P × light × F03, K. May continue F02 clip. No need for a separate capture for every matching substring field. | MISSING |
| F04 — provider filter inclusion and exclusion | **Changed** inside matching provider; boundary outside it | Two eligible providers, Claude and another. Existing unstarted thread permits switching. With the other provider filter selected, exact Claude legacy query yields No matching models; choose Claude → the target appears; All providers also finds it. Provider filtering must not be reset by typing. Filtered headers have no collapse affordance. | B plus an unstarted thread created through app/RPC, without a provider turn. Composer model control → `ThreadSettingsSheet` with environment/thread/draft params. Use real filter menu → Provider. Provider-locked variant is inventoried below. | **recording**, P × light × F04, K. | MISSING |
| F05 — Favorites cannot leak a non-favorite legacy hit | Boundary: Favorites already bypassed legacy collapse | Target initially not favorite. Favorites + exact query → No matching models. Return All providers, find target, tap Add to favorites, return Favorites → target alone with filled star. Clear query: favorite legacy remains. Remove favorite → No favorite models when query empty, No matching models when query non-empty. | B; UI star controls, filter menu → Provider → Favorites. Can seed star via `setModelFavorites` RPC, but record the UI transition at least once. | **recording**, P × light × F05; include filter menu state and settled persistence. | MISSING |
| F06 — explicitly hidden target stays excluded; manager restores it | Boundary: hidden filtering precedes helper | Exact-query target visible. Enable Manage hidden models, Hide target using eye control. It remains only in manager, with Hidden marker and disabled model radio; eye becomes Show. Disable manager: search is empty/No matching models even with Show legacy on or target favorited. Re-enable manager, Show target, disable manager: search finds it again. | B; scroll to Catalog → Manage hidden models. Use `Hide model: Claude, Claude Opus 4.8` / `Show model: Claude, Claude Opus 4.8`. Target is bundled/non-custom (`canHide: true`). Optional seed via `setModelsHidden`. | **recording**, P × light × F06; include pending→settled mutation and return to list. | MISSING |
| F07 — legacy toggle and search independence | Boundary: explicit bypass unchanged | Empty search: Show legacy on displays target, off hides it. With exact query present: target stays when toggle goes off, while menu check changes. Clear query with toggle off: target disappears. Manage hidden off; target not selected/favorite/hidden. | B; Filter models → Show legacy models. On Android also exercise Catalog → Legacy models as the same state/control alias in this recording if Android is chosen. | **recording**, P × light × F07, K. | MISSING |
| F08 — stage, clear, cancel/save, and selected exception | **Changed**: a newly searchable hit becomes a pending selection | Scheduled-task editor starts with a current model. Search target, press it: selected indicator and Save appear; clear search: staged legacy remains via `isDisplayed`. Cancel: reopen retains original current model. Repeat and Save: editor Model changes; reopen with empty search retains selected legacy. No scheduled task must be created or run. | B; Settings → Scheduled tasks → New task → choose environment/project → Model (`SettingsScheduledTaskModel`). This covers the third picker caller. Existing-thread/draft use the same pending helper; queued edit differences are inventoried below. | **recording**, P × light × F08; show returned editor and reopen, not only selected radio. | MISSING |
| S01 — missing caller context | Unchanged; no minimum | Existing-thread wrapper without selectedThread renders blank sheet; scheduled picker without editor renders “Open a scheduled task form first.” These are distinct wrapper fallbacks, not model-search empty results. | Not a normal stable navigated flow; labelled harness for absent context, or real deletion/navigation race if deliberately reproduced. | **recording** for deletion/navigation race; still only for a labelled static harness. No minimum. | INVENTORIED; NOT CAPTURED |
| S02 — no eligible models | Unchanged; no minimum | No provider groups, no query/filter → No available models + Options/Catalog; no legacy toggle when `hasLegacyModels` false. Null environment also removes pull-to-refresh and keeps favorites unavailable. With an active query/filter → No matching models. | Real fresh app with no eligible installed/authenticated-or-unknown provider and no selected fallback; or exported-picker harness with null environment/selection and empty groups. | **recording** if toggling query/loading/connection; static harness still permitted for a fixed empty state. No minimum. | INVENTORIED; NOT CAPTURED |
| S03 — no favorites / no match | Unchanged, already folded into F05/F04 | Favorites + blank/whitespace query → No favorite models; any non-empty unmatched query → No matching models. All groups with zero matches and no notice disappear. | Real B; remove stars and choose Favorites, or use unique nonmatching query. | **recording**, covered by required clips; no extra viewport/theme. | MISSING; COVERED BY F04/F05 PLAN |
| S04 — provider collapse/count and row cardinality | Header behavior unchanged; changed membership covered by F02/F03 | Primary drivers Claude/Codex/Antigravity default expanded; secondary default collapsed unless it contains applied selection. Expansion override reverses default. Collapsed shows count and chevron; expanded hides count and renders rows. Search or any provider/Favorites filter forces expansion and removes toggle. Zero rows+no notice omit group; one versus many gives single versus first/middle/last corners. | Real B + an enabled eligible secondary provider for default-collapse/override; if unavailable, labelled harness with secondary group and known count. Do not fabricate availability. | **recording** for expand/collapse/search; cardinalities folded into F02/F03. Secondary-driver-only case supplementary. | PARTLY PLANNED; NOT CAPTURED |
| S05 — update-required notice only / notice plus models | Unchanged; no minimum | Group with matching `updateRequiredModels`, with either zero visible hits (header + notice instead of empty) or rows plus notice. Hidden when group collapsed; omitted in Favorites. Query filters notices by gated model name/slug. Names count 1, 2, or >2 changes notice sentence; long notice wraps. | Real older eligible CLI can yield current-model upgrade notices. Opus 4.8 specifically is legacy and is omitted from Claude's update-required list; do not use an old CLI as proof of its search visibility. Exact notice shapes/counts use labelled harness. | **recording** for query/filter/refresh transitions; fixed notice harness may be still. No minimum. | INVENTORIED; NOT CAPTURED |
| S06 — dense/decorated/disabled row | Unchanged; Legacy and selected parts covered by F02/F08 | Long label/subtitle; optional Default/Legacy badge; Hidden or Unavailable text; selected/unselected; favorite filled/empty; `canHide` true/false; hidden mutation pending; favorites loaded/unloaded. Hidden/unavailable disables row, custom model has no hide eye. iOS one label/subtitle line + trailing check; Android two lines + leading radio + selected background/minimum height. `shortName`, badge metadata, capabilities and supported modes affect upstream choice/options, not a new catalog-row field here. | Real B covers normal/legacy/favorite/selected/hidden. Long custom model via provider config can cover long text/subtitle only when driver supports it. Unavailable fallback via disabled selected provider; exact combined decorations/loading states use labelled harness. | **recording** for selections/mutations/loading; static labelled harness for fixed typography/decorations. No extra required capture. | INVENTORIED; NOT CAPTURED |
| S07 — settings/provider loading, refresh, and failure | Unchanged; no minimum | Initial favorites disabled until settings/preferences load; normal list has no independent loading skeleton. Pull-to-refresh spinner only with environment; duplicate refresh ignored. Refresh failure alert “Could not refresh models”. Settings save failure/pending state; provider disappears while selection pending → Save alert “Model unavailable”, sheet remains. Animation onLoad enables subsequent fades (140/120ms) and item/layout transitions (180ms); initial rows do not enter-animate. | Real isolated RPC/app for refresh or deliberate provider removal; deterministically held pending/failure gates and exact alerts use labelled harness. Catalog fetch can update data after startup; record the actual server snapshot. | **recording** mandatory for loading, refresh, failure recovery, provider disappearance, or animation. No minimum because unchanged by this diff. | INVENTORIED; NOT CAPTURED |
| S08 — options and nested choice routes | Unchanged; no minimum | Pending model with capabilities → select/boolean descriptors; no capabilities → only Runtime. Select row with/without value, boolean on/off; descriptor menu with selected/description/last row; runtime compatible choice set; missing descriptor → blank submenu. Prompt-controlled Ultrathink changes value or blocks a change with alert. Pending option edits stay with pending model; applied edits update draft. ChatGPT sharing status is conditional on selected provider and absent for Claude/null provider. | Real B for Claude descriptors and runtime; type prompt text for prompt-controlled traits. Missing descriptor/null capabilities/exact runtime arrays via labelled harness. Sharing banner requires a real sharing provider or labelled harness. | **recording** for submenu navigation, switches, prompts, pending changes or alerts. Static harness permitted for fixed choice layout. No minimum. | INVENTORIED; NOT CAPTURED |
| S09 — theme, native search/header and width alternatives | Unchanged; no minimum beyond P | Light/dark token colors; iOS glass/solid header availability; native mail toolbar when supported and verticalBarEdge none, otherwise iOS header search + bottom filter/search slot; Duo rail layout, safe-area and keyboard insets; Android explicit search/clear and top filter/Cancel/Save, centered maxWidth 720, larger list estimate; portrait/landscape and long-list overflow. Raw nonempty whitespace shows Android clear control despite normalized blank membership. iOS always Done/Save confirmation; Android Save only for pending model. | Light/dark via Settings → Appearance; phone rotation/keyboard through real app. Supplied phone cannot be assumed to exercise a Duo/tablet rail or unsupported older iOS capability; labelled harness or separately available appropriate native device. Do not claim CSS resizing a phone proves these native branches. | **recording** for keyboard/search/menu/rotation/dismissal. Optional dark F02/F03 and Pixel 9 F02/F06/F07 smoke. No full cross-product is justified by unchanged style/platform code. | INVENTORIED; NOT CAPTURED |

Minimum capture allocation

- **Required set:** P × light × {F01,F02,F03,F04,F05,F06,F07,F08}. P is one of the two permitted native phone devices; use the same device/theme and seed for paired baseline/head evidence. All rows are recording rows. Settled still frames may accompany clips but cannot replace them.
- Four head clips may cover this without redundant recordings: new-task search/reset/broad matches (F01–F03); existing-thread provider filter (F04); new-task Favorites/hidden/toggle (F05–F07); scheduled editor selection/cancel/save (F08). Restore target preferences between sequences so the next row's preconditions remain true. An all-in-one clip is also valid if each row has a timestamp and visible precondition.
- Pair F02 and F03 with baseline recordings at the supplied base using the same catalog and settings. Baseline should omit unstarred/unselected legacy hits; head should show them. No new baseline checkout or capture was created in this read-only pass. The lead owns capture setup.
- Optional extra, not a condition of this one-branch proof: dark-theme F02/F03 and the other platform F02/F06/F07. Platform-specific UI paths are enumerated rather than asserted tested. If either platform produces a concrete discrepancy, its affected row becomes required.
- Capture metadata must name revision, fixture ID, caller, device/OS, actual viewport, theme/text scale, query/filter values, target selected/favorite/hidden flags, provider instance ID/driver, installed/version/auth status, and recording timestamps. Keep pairing credentials and tokens out of clips/log uploads.

Real-app base seed B and controls

1. The lead starts an isolated fresh server with `T3CODE_DEV_SEED=0 vp run dev` from its own checkout, using a new task-specific home if that checkout already has a database. The flag prevents copying live data; it does not erase an existing database. Do not point at or copy from live userdata. No copied settings or credentials are required for bundled catalog construction. Pair the Expo dev client on the pooled iPhone 17 Pro or Pixel 9 using the existing mobile pairing workflow. Do not launch a provider turn or save an active schedule to obtain a picker.
2. Add one project through the app's Add project flow using a disposable directory. Open a new-task draft and its composer model control. Make a current Claude model selected; Opus 4.8 must initially be unselected, unfavorited, unhidden. Keep Show legacy models and Manage hidden models off and provider filter All providers. For F04, make a second eligible provider available and create an unstarted thread through the app or typed `orchestration.dispatchCommand` (`thread.create`), so provider switching remains allowed. No SQL fixture or copied database is needed.
3. Preflight the actual `server.getConfig` / subscribed server-config snapshot: Claude default instance `claudeAgent` (or the actual chosen instance), enabled/project-enabled, installed true, auth authenticated or unknown, and model `claude-opus-4-8` with `isLegacy: true`. A successful CLI version path requires version >= 2.1.154 for that model. An unknown/missing/older version may prune it on the normal successful version-check path. The server can expose all bundled models even on some failed-version paths, but that failure is not a substitute for recording exact preconditions. Bundled model names are fetched without provider credentials, and runtime manifest refresh can replace the bundle, so preserve the actual snapshot/manifest identity used for both revisions.
4. **Credential-free qualification:** a normally signed-out CLI with capabilities reporting `tokenSource: none` and no key/account fields yields unauthenticated and is filtered out. A failed/unavailable capabilities probe with installed CLI gives auth unknown and version-filtered bundled models, which mobile does admit. A real host may already have provider authentication even with a fresh T3 database; describe that accurately. If a clean credential-free host cannot produce an eligible catalog, mark the real-app capture blocked on eligibility rather than silently signing in or pretending the initial snapshot works.
5. **Deterministic alternative is labelled harness evidence:** an isolated provider executable fixture can print a supported Claude version for `--version` and fail the capabilities initialization promptly, producing installed=true/auth=unknown via the real server's existing boundary. Configure only that disposable server instance through `server.updateSettings` with `providerInstanceMutation` upsert and Claude `config.binaryPath` (and isolated `homePath`). This uses the unmodified mobile app and real RPC, but the provider is synthetic: label it “real Expo client, bundled Claude catalog, provider-status fixture”, never genuine authenticated Claude evidence. No such executable was written or run in this pass. Do not intentionally wait for a timeout; prompt failure suffices. For otherwise unreachable exact visual states S01/S05–S09, use a separately labelled exported-picker harness with fixed props/environment atoms; it is not real-app proof of F01–F08.
6. Settings can be seeded by the same RPC the UI uses: `server.updateSettings` payload `{patch:{setModelFavorites:[{provider:<instanceId>,model:"claude-opus-4-8",favorite:true|false}]}}` and `{patch:{setModelsHidden:[{provider:<instanceId>,model:"claude-opus-4-8",hidden:true|false}]}}`. Use actual instance IDs and wait for the subscribed settings update. Provider instance upsert is `{patch:{},providerInstanceMutation:{operation:"upsert",instanceId:<id>,instance:{driver:"claudeAgent",enabled:true,config:{binaryPath:<path>,homePath:<isolated-dir>}}}}`; preserve any other needed instance fields. A second Claude instance can provide distinct provider-filter choices, but cannot stand in for a different driver's platform/provider presentation. No fixture mutations were made here.
7. **Show legacy models:** open Filter models (iOS native search/filter toolbar or fallback bottom toolbar; Android top-right filter) → Show legacy models. This action exists only when normal `props.providerGroups` has at least one legacy model. Android additionally exposes Catalog → Legacy models at the bottom of the list. It is session-local and resets on remount.
8. **Manage hidden models:** scroll the list to Catalog → Manage hidden models. This is separate from the filter menu and rebuilds the catalog with hidden entries while also bypassing legacy collapse. **Hide a model:** enable that switch, then use the model's eye button; accessibility name is `Hide model: <provider label>, <model label>`. Hidden entries have a Show button, Hidden marker, and disabled model selection. Custom models (`canHide: false`) have no eye control. Turn the manager off to verify exclusion. If all legacy entries are hidden, `hasLegacyModels` is derived from the normal groups, so the legacy action may be absent even while the manager can show them.
9. **Favorites:** tap the row star; only enabled after favorites/settings load. Then Filter models → Provider → Favorites. A favorite legacy model is deliberately visible even with an empty search and Show legacy off. Favorites still excludes non-favorites during a search. Hidden filtering still applies outside the manager.
10. **Routes/caller differences:** new draft uses `NewTaskThreadSettings` with linking path `new/draft/settings`; existing composer uses `ThreadSettingsSheet` with environmentId/threadId/draftKey (no direct public linking path registered); scheduled editor uses `SettingsScheduledTaskModel` after Settings → Scheduled tasks → New task or existing task → Model. New draft and existing thread restore remembered model options; scheduled picker does not pass restoreSelection or providerInstanceId. Existing thread can lock catalog to the selected provider's continuation family; queued edits read the queued prompt/draft key, and if queued editing ends while the sheet is open it falls back to thread draft. These data/entry differences are inventoried, but they do not add a separate visual branch to the changed query predicate. An absent selected thread or absent scheduled editor has S01 fallback. Web/desktop, MCP tools, scheduled execution, provider adapters and wire contracts have no changed search code in this diff.

Complete render/data/interaction inventory (collapsed without a Cartesian product)

- `ThreadSettingsSessionProvider` has children-only rendering, no standalone view. environmentId/config/settings/preferences/connection-generation govern load/migration and mutation availability; normal versus manage-hidden groups govern input membership. selectedModel/pendingModel controls checkmarks, legacy exception, descriptors, runtime choices, Save, and prompt routing. Reported selection changes option labels; restoreSelection preserves remembered options. projectId/lockedProviderInstanceId filter provider inputs. Favorite key set controls retention/order/star/filter, while pending hidden keys disable eyes. All session-local filters start false/null/empty; remount reset is part of F08. Missing context for `useThreadSettingsSession` throws rather than a user-facing loading branch.
- `buildThreadSettingsCatalogItems` is the only changed render-building path. It drops nonmatching provider groups first, runs the new helper, computes an optional upgrade notice, drops zero-result groups without a notice, decides primary/applied/default/override/narrowed collapse, sets model count, and emits provider/model/notice items. Model index zero/last controls corners and separators. Favorites and manager/Show legacy previously already bypassed legacy collapse; only normalized non-empty query adds a new bypass. Match inputs are full label, subtitle, raw model slug, provider label; aliases/shortName/provider driver are not independent query fields. Stable favorite partition preserves source order within favorite/nonfavorite groups.
- `ProviderHeader` branches on collapsible; collapsed count and chevron-down versus expanded chevron-up; noncollapsible header lacks these. Driver/icon URL and arbitrary provider label control icon/text. `ThreadSettingsProviderListHeader` delegates session toggle. `ThreadSettingsModelListRow` delegates select/star/eye, pending state, first/last, favorite readiness and displayed check. Full row layout branches are S06; the new helper has no new props, colors, badges, or row renderer.
- `ThreadSettingsMainContent` item branches: provider, model, notice, empty and always-present options. Empty copy distinguishes no favorites / no match / no available. Search Android header/clear branches, environment-dependent refresh, Android width and item estimate, onLoad animation readiness, scroll/keyboard dismissal, list-item fade/layout timing are S02–S09. isRefreshing prevents concurrent refresh and drives spinner; failed refresh opens alert. List changes can produce a short underflowing result list or long scrolled list: F02/F03 must record the transition and last accessible row/options.
- `ThreadSettingsOptionsItem` renders optional selected-provider ChatGPT status, zero/many select/boolean option rows, Runtime, Manage hidden and Android-only conditional Legacy models switch. Safe area plus iOS bottom-toolbar inset affects scroll end. Select disclosure value present/absent and row separators; boolean switch state; descriptor enter/exit readiness; last-row flags affect layout. Selection of a new legacy model can change option count and runtime choices: inspect in F08, detailed unchanged choices in S08.
- `ThreadSettingsModelsScreen` has filter menu Provider submenu with All/Favorites/every present provider; Show legacy conditional on hasLegacyModels; checked menu states and active filter icon depend on providerFilter/showLegacy, not search or manageHidden. Commit failure leaves sheet open. Platform-specific header, search toolbar, fallback search cancellation, Save/Done, menu dispatch and submenu push paths are S09. Every search/filter/star/eye/toggle/select/Save/Done/Cancel/pull-refresh/scroll/keyboard interaction is a recording row; no timing-dependent claim can be established by a still.
- `ThreadSettingsChoiceContent` and screen/navigator branches: runtime versus descriptor, missing descriptor blank, selectable rows, prompt-controlled Ultrathink selected/blocked, selected choice, description present/absent, last row, Haptics, update then goBack; Android back header/width; iOS native header, glass/solid background, theme tint, navigation transitions. See S08/S09. Picker navigator always starts models; context missing throws rather than rendering a special empty state. Public `ThreadSettingsPickerScreen` always wraps the same session and navigator; its three callers and their no-context branches are listed above.
- Width/platform/theme are dimensions of these existing paths, not a reason to multiply F01–F08 over all combinations. Native phone portrait + one fixed theme is the minimum for this membership-only change. Supplementary long text, dark, other-platform, old-iOS/fallback and wide rail captures must be labelled precisely if provided.

Coverage and limits

Completed: exact requested diff, pinned-head verification, production caller tracing, input/query/filter/collapse/hidden/selection paths, bundled legacy classification and version/auth gates, mobile navigation/control locations, minimal real-app seed plan, fixture/capture matrix, read-only worktree check. Not run: tests, CI, runtime server, native app, device capture, or external PR discussion audit; they are outside this pre-capture fixture pass. All capture statuses remain missing or inventoried, never passed. Disk had 19 GB free at both checks; no under-5-GB stop condition. No process cleanup is needed because none was started. Written material is confined to `/tmp/lead-27587-r1/proof-audit-1/`.

FRICTION: none

VERDICT: INSUFFICIENT F01, F02, F03, F04, F05, F06, F07, F08 — record all required rows; pair F02/F03 with the supplied baseline.
