# Issue 23245 — proof-audit matrix, pass 1

Status: refreshed pass-1 MATRIX audit only. Read-only complete four-file diff at HEAD b065ad799c5bfda2027d7d68327bb6bcd1c49666 against current origin/main 3b95e7831b11b17627dff16843bc087bfca397ec, /home/ubuntu/wt/issue-23245. Working tree was clean at inspection. No browser, capture, server, repository edit, push, or public comment performed. Pass 2 separately checks captures.

Provenance warning: historical before SHA a50cd989aee06ed0762e26027eadf35933eb52ac differs from current merged base 3b95e7831b11b17627dff16843bc087bfca397ec. Manifest also records historical after 33e77985c707af61bac392e8adce1e01987ecf14, not audited HEAD b065ad799c. Evidence agent is resolving this mismatch. The two changed UI files have no diff between historical before and current merged base, but that does not establish whole-client equivalence or validate old captures.

## Capture contract

Run the real web client served by the real dev server. Lead reports localhost:7888 uses isolated .t3; evidence capturer must independently confirm checkout/state and use a fresh own browser. Do not reuse/drive lead's browser or mutate live ~/.t3/userdata. Reviewer launched no server/browser and took no captures. Lead's reproduction is supplied evidence, not reviewer reproduction.

Each required row runs in all four cells: 1440px/light, 1440px/dark, 390px/light, 390px/dark (record height, browser, commit/diff fingerprint). Use stable heights such as 900 desktop and 844 narrow. 390 means responsive WEB, not native mobile. Explicitly set theme, do not rely on OS preference. Each row needs before/after endpoint screenshots; every interaction/time row requires continuous video from before the trigger through settled result, including close/reopen/reload when specified. No arbitrary sleep assertion; wait for real UI data to settle. Label artifacts by row, width, theme, and before/after. Never expose auth/startup URLs or credentials.

Baseline comparisons: reproduce R1 and R3 against current merged base 3b95e7831b in a separate isolated state/check-out or before applying the patch, then repeat identical fixtures against patched code. Do not revert the implementer's worktree. R2/R4 provide controls and recovery behavior; pair baseline/patched endpoints where practical. An empty Favorites view intentionally selected by hand is not the bug; the bug is initial default selection.

## Minimal fixture/capture table

| ID | Changed path / input branch | State data and seed approach | Interaction and expected proof | Affected | Recording | Required capture cells |
|---|---|---|---|---|---|---|
| R1 | Picker initial fallback; favorites nonempty but none included by shouldIncludeModelPickerOption | Isolated dev settings: favorite actual OpenCode Big Pickle using UI, record actual instance ID/slug, then disable OpenCode; keep ready Codex active with reported selectable models. Add one stale saved favorite for absent instance and one absent model slug for an enabled instance using the committed isolated-state seeder below if UI cannot produce them. All favorites must be unpickable. Do not add custom models as a substitute: caller merges those back into reported models. No lock, no active setup or unavailable-model guard. | From closed picker, open composer picker; initial section must be active Codex with real rows, not empty Favorites. Select a Codex model; trigger updates. Close/reopen to prove initializer remount behavior. Search that available model and clear search, then keyboard-select once to check usable fallback. Baseline opens empty Favorites. | Yes: initializer false branch, absent entry/map/model and disabled-ready predicate | Required: open → settled rows → selection → close/reopen → search/clear/keyboard result | 1440 light/dark; 390 light/dark |
| R2 | Picker positive control; at least one included favorite among stale favorites | Reset R1 fixture; favorite one reported, visible, ready Codex model via real star control, retaining stale OpenCode favorite. Optional empty-favorites substep is obtained by clearing this favorite and stale favorites through Settings. | Freshly open: Favorites initially selected and available Codex favorite appears exactly once; select it, reopen, unstar, close/reopen with only stale favorites and observe active Codex fallback. Once all favorites removed, reopen and confirm same active-provider default. Record that stale favorites never become selectable. | Yes: initializer true branch, mixed favorites, empty-array control; existing star/selection integration | Required: star → fresh open → select → unstar → reopen → empty-favorites reopen | 1440 light/dark; 390 light/dark |
| R3 | Settings missing favorites; disabled provider, models=[] | Reset OpenCode Big Pickle favorite while enabled, then disable provider through actual Settings. Keep saved favorite and no custom models; confirm Models receives zero reported models. Use same actual slug as R1. | Navigate Settings → Providers → disabled OpenCode → Models. Patched view shows Favorites group and slug-only starred synthetic row despite `0 models · 1 favorite`; baseline has no removable star. Click accessible Remove … from favorites; row and favorite count disappear, empty reported-model message returns. Reload/revisit confirms removal persists; composer reopen with ready Codex remains usable. Do not re-enable provider to clear favorite. | Yes: appended synthetic row, name=slug, isCustom=false, capabilities=null; zero-model empty state and removal | Required: navigation → starred row → unstar → empty state → reload/revisit → picker reopen | 1440 light/dark; 390 light/dark |
| R4 | Settings missing favorite on enabled provider mixed with reported models; reported favorite de-duplication | Using the committed isolated-state seeder below, save a distinctive missing slug (e.g. audit/missing-model-23245) for enabled ready Codex WITHOUT adding it as custom model; keep one genuine reported Codex model favorited. Prefer >8 reported models to expose Filter models; record actual count. This differs visually from R3: normal models, All group, bulk control and possibly filter remain. | Navigate enabled provider Models: missing slug appears once starred under Favorites alongside exactly one existing reported favorite; no duplicate reported row, no custom badge/editor/delete. If filter exists, filter exact missing slug then clear. Unstar missing row; it disappears while reported models/favorite survive. Unstar/re-star reported favorite as a normal control and reload to verify persistence. If >8 unavailable, log filter substep N/A without fabricating provider catalog. | Yes: reportedSlugs.has true/false, mixed groups/counts/filter, missing-row removal vs reported-row survival | Required: before → filter/clear if present → missing unstar → reported unstar/re-star → reload | 1440 light/dark; 390 light/dark |

## Code-derived scope and equivalence

Complete diff: ModelPickerContent.tsx (+17/-1), ProviderModelsSection.tsx (+7/-1), scripts/fixtures/issue-23245.json (+14), scripts/seed-issue-23245.mjs (+37): 75 insertions, 2 deletions. Both product changes remain local to shared web rendering; fixture/seeder are explicit review evidence requirements.

- ModelPickerContent.tsx:426–446 changes only useState initializer, not later synchronization. A genuinely new popup mount is essential. The predicate is the same one used for flatModels. Ready means enabled && isAvailable && status==='ready'. Missing entry, missing options map/empty array, absent slug and disabled entry all produce no included favorite and are combined in R1. A reported ready favorite is R2. Hidden favorites absent from modelOptions are equivalent to absent slug; do not separately multiply screenshots.
- Existing early guards remain: lockedProvider, active selectable-unavailable OpenCode/Antigravity, active provider setup choose active provider before favorite scan. These are unchanged paths; source inspection is sufficient for this two-branch patch, no separate provider/auth fixture zoo required. Antigravity default sentinel is excluded by the shared predicate. The word 'pickable' does not newly promise getModelDisabledReason===null: that callback and locking behavior are unchanged.
- ProviderModelsSection.tsx:138–146 adds synthetic models only when slug is not reported; existing grouping/filter/row controls render them. Count still uses original models.length, intentionally allowing 0 models plus a favorite. R3/R4 test both empty and populated layouts. Settings canWritePreferences gates stars; captures use a writable environment. Read-only behavior is unchanged and does not require a second permission setup. Synthetic row show/hide/move controls are inherited; no claim that a show toggle restores an absent model. No custom CRUD/reorder campaign is justified.
- Caller chain: ProviderModelPicker → ModelPickerContent is shared by composer (ComposerFooterControls), ProjectDefaultsSettings, ScheduledTasksSettings, SettingsPanels text generation, and SourceControlWritingSettings commit/review-guide pickers. All receive the same environment favorites and initializer; one composer integrated pass covers changed rendering. Other callers are source-equivalent for the changed branch, no separate screenshot per caller. Settings chain: ProviderSettingsPanel → ProviderInstanceCard → ProviderModelsSection; deriveProviderModelsForDisplay merges live reported and configured custom models first, so remove/disable a genuine catalog model or seed a preference-only slug for missing-model proof.
- Web is directly affected; Electron shares this web code, no shell/IPC change. Native mobile has separate components and is not changed. Provider readiness uses a generic shared predicate; OpenCode reproduction plus Codex positive control is sufficient. No contracts, server services, MCP, scheduled execution, connection transport or permission changes. Preferences belong to selected environment and persist server-side; reload proof guards against local-only optimistic success. No need to send a provider turn.

## Seed / capture handoff

The full diff includes scripts/fixtures/issue-23245.json and scripts/seed-issue-23245.mjs. These committed fixture/seeder files are explicitly required by the task and override the general scratch-artifact convention. Keep them in scope; no extra product behavior or matrix rows follow from their inclusion.

The seeder writes isolated userdata/settings.json atomically, replaces favorites with the named fixture, and forces the opencode instance disabled. It refuses the canonical home .t3 directory and requires a realpath basename .t3. It does not create provider readiness, models, project/thread, remove custom models, or reset hidden preferences. Stop the isolated server before seeding and restart/reload through the real settings path; confirm actual resulting settings in UI. Do not execute it against live state. No seeder was run by this reviewer.

R1 seeds disabled opencode/big-pickle plus absent-instance and absent-Codex-slug favorites. R2 seeds only the stale OpenCode favorite: the real reported Codex favorite must then be starred in UI before the positive-control fresh open. R3 seeds disabled OpenCode favorite; separately confirm reported models are empty and custom models absent. R4 seeds the missing Codex slug plus gpt-6.1-sol; confirm the actual Codex instance ID and real reported gpt-6.1-sol catalog entry, replacing the setup through legitimate settings/UI if they differ. Never use a fabricated provider catalog or custom missing-model entry.

Record nonsecret fixture values: environment, instance IDs, exact slugs, enabled/readiness state, favorites, hidden preferences, actual reported count and custom-model absence. Restore independently per width/theme. Every interaction and time-dependent proof belongs in a continuous recording, including setup UI transitions used as evidence, trigger, settled result, removal, close/reopen and reload. Stop/reset/start and provenance notes must identify any discontinuity; screenshots cannot substitute for recording. R4 filter is N/A only when the real catalog does not exceed eight models. Capture CSS innerWidth/innerHeight and devicePixelRatio directly; filenames and exported raster dimensions are not viewport proof.

## Machine-readable matrix (JSON)

```json
{"issue":23245,"pass":1,"status":"matrix_sufficient_capture_audit_deferred","base":"3b95e7831b11b17627dff16843bc087bfca397ec","worktree":"/home/ubuntu/wt/issue-23245","branch":"fix/issue-23245","cells":[{"width":1440,"theme":"light"},{"width":1440,"theme":"dark"},{"width":390,"theme":"light"},{"width":390,"theme":"dark"}],"rows":[{"id":"R1","fixture":"only_unpickable_favorites","affected":true,"interaction":true,"time":true,"recording":"required","seed":"UI favorite Big Pickle then disable; isolated settings absent-instance/absent-slug favorites; active ready Codex","expect":"initial active provider rows; selection/search/reopen work"},{"id":"R2","fixture":"mixed_available_and_stale_then_empty","affected":true,"interaction":true,"time":true,"recording":"required","seed":"R1 plus real reported Codex favorite; remove favorites for empty control","expect":"initial Favorites; removing last available favorite restores fallback on fresh open"},{"id":"R3","fixture":"disabled_provider_zero_models_saved_favorite","affected":true,"interaction":true,"time":true,"recording":"required","seed":"UI favorite real Big Pickle then disable OpenCode, no custom models","expect":"slug-only removable favorite; unstar disappears and persists"},{"id":"R4","fixture":"enabled_provider_missing_and_reported_favorites","affected":true,"interaction":true,"time":true,"recording":"required","seed":"isolated preference-only missing slug plus actual reported favorite; no custom missing slug","expect":"synthetic row and no duplicates; filter if present; unstar only removes missing row"}],"all_rows_all_cells":true,"baseline_required":["R1","R3"],"captures_by_reviewer":false,"repository_edits":false,"new_tests":false,"head":"b065ad799c5bfda2027d7d68327bb6bcd1c49666","historical_before":"a50cd989aee06ed0762e26027eadf35933eb52ac","complete_diff_files":4,"committed_seeder_required":true}
```

FRICTION
- Historical before a50cd989ae differs from current merged base 3b95e7831b; manifest after 33e77985c7 differs from current HEAD b065ad799c. Evidence agent is resolving provenance. No old artifact is certified by this matrix audit.
- Manifest reports disputed desktop CSS viewport provenance (1280x800 PNG exports vs 2880x1800 recordings; requested 1440x900, no surviving innerWidth). Pass 2 must verify actual viewport, all 16 cells, recordings and required interactions, including R4 narrow cells. Artifact names/counts alone are insufficient.
- Seeder fixture assumptions require real provider IDs/catalog validation, stopped isolated server, UI confirmation and independent reset per cell. R2 JSON alone is not the positive-control fixture. Seeder does not prove empty custom/hidden settings or readiness.
- No capture/browser/server, tests, repository edits, push or comments performed. This is a matrix sufficiency decision, not implementation approval or capture approval.

VERDICT: SUFFICIENT — MATRIX ONLY. R1–R4 × 1440/390 CSS px × light/dark (16 cells), with continuous recording for all interaction/time proofs, still covers the complete four-file diff. No additional provider/client/caller permutations are justified by this diff. Separate pass 2 must decide capture sufficiency.
