# Proof audit, pass 1 — partial-word pull request search

Issue: [#26950](https://git.cafe/pingdotgg/t3code/issues/26950). Scope: branch `fix/issue-26950` in `/home/ubuntu/wt/issue-26950`, requested head against the explicitly supplied main base. HEAD was verified as the requested revision; that base is its merge base. The checkout's current `main` ref has advanced, so this audit deliberately uses the supplied base rather than today's `main`. No PR exists yet.

## Findings first

- No implementation defect established by this source-only proof audit. The patch merges locally matching, already-loaded baseline rows with the current answer and applies involvement and locally decidable filters to both. It preserves host-only text hits and suppresses duplicate identities.
- **Recordings are required.** The reported defect occurs after the 250 ms debounce and host response. A settled still cannot prove that a matching row survived. Every required fixture below is `recording`, including controls driven by typing or filter changes.
- **Pass 1 produces a capture plan, not approved visual proof.** No captures were supplied or produced. Every required row is PENDING CAPTURE. The terminal sufficiency verdict concerns the completeness of this proportionate fixture plan only.
- The eligible supplement is the separate text-free baseline read, limited to 99 rows per environment request, under the current state, involvement, project, host, and menu filters. This is not a promise to find every substring match in repository history or in previously loaded continuation pages.
- Typed valid `checks:` and `status:` both parse to `filters.checks` and disable supplementation. Menu checks do **not** disable it: the baseline request already includes menu checks. This asymmetry needs two fixtures.
- Host-first ordering applies before grouping; rendered involvement groups have their existing priority. Compare host order **within each group**, using the default sort. Explicit newest/oldest/largest/smallest sorts may reorder rows afterward by design.
- The added logic tests cover union membership, host-only hits, one overlap, host-first append order, and an author filter. They do not establish the page-level checks/status gate, rendered grouping/counts, or the timed transition. Tests were inspected, not run during this read-only fixture-design pass.

## Render-path inventory and collapse

Only `PullRequestsPage` is a changed render component; its props are empty. `pullRequestGroups` is a changed pure helper, and the third changed file is its test. Repository search found one production caller of each: route `/_chat/pull-requests` mounts the page at `/pull-requests`; the page calls the helper once. Composer PR suggestions use other helpers and are not changed. Web and desktop share this route; mobile is untouched.

| Code path / input | Visible consequence | Fixture / disposition |
| --- | --- | --- |
| `text.length === 0` | Existing updated-time input ordering; default readiness sort; authored/review-requested/other groups; priority partitions only when the raw typed query is empty | F3 recovery. No new supplement for blank text or qualifier-only queries. |
| Nonempty text; `answeredLocally` false versus true; provider `searchesOnHost` false versus true | Local substring narrowing while typing/carried; authoritative host rows retained after answer only for searchable hosts | F1 transition; F2 host-only hit. Non-searching hosts keep existing local matching; no separate platform/theme capture. |
| Eligible baseline undefined/empty, or zero missed rows | Existing answer unchanged | F5 veto; F3 true empty; F2 duplicate. Cold baseline absence covered by lifecycle observation below. |
| Eligible baseline contains one or many missed matches | Nonempty list replaces wrong empty state; supplement ranked and appended | F1 one-row case; F2 multirow case. |
| Duplicate identity `(environmentId, host, repository, number)` | Overlap appears once; host version wins | F2. Equal PR numbers on different repositories/hosts/environments are distinct identities; no identifier-only dedup claim. |
| `hasLocalFilters` false versus true | Unqualified union versus narrowed union | F1/F2 versus F4. Local filters are author (including viewer resolution), draft, review, label AND/OR groups and excluded labels. Checks are not locally decided. |
| Involvement all versus authored/reviewing; known or missing viewer | All: nonempty labelled groups and counts; role filter: one unlabelled list; authored requires a known matching viewer, reviewing uses `viewerReviewRequested` | F2 all, F4 role-scoped. Authored/reviewing share the unlabelled layout; no Cartesian capture grid. |
| `baselineEmpty`, priority partitions available/unavailable, held partitions, `partitionsWanted` | Existing group partition paths; text search disables extra partition reads, then clear restores them | F3 clear/recovery. No independent partition machinery added. |
| Menu checks versus valid typed checks/status | Supplement remains eligible versus disabled | F6 versus F5. Invalid/unrecognized checks values remain literal query text, outside the new valid-qualifier gate. |
| Capability unknown/unsupported; first load | Loading ghosts / unsupported-server error | Unchanged gates; observe initial load in F1. No independent mandatory unsupported-server capture. |
| `shownCount === 0`, query unsettled/carried versus settled | Search ghosts versus true “Nothing matches”; clear and check-again controls | F3. Query length over 48 truncates empty-state copy; no copy/layout change. No-project and blank filtered/unfiltered empty states are unchanged. |
| List error with zero versus positive shown count | Unavailable state versus existing warning alongside retained rows | Conditional lifecycle observation: if an error occurs while capturing, preserve it and classify the run; do not treat it as a valid successful-host F1/F2 capture. A controlled error fixture is optional, not minimum bug proof. |
| Truncated answer; pending continuation; cursor present/absent; maximum page size | Existing load-more button / spinner / narrow-search message | Observe if present in F2; no pagination changes. Supplement remains first-page baseline only. |
| Multiple hosts/environments; provider configured/error; project/host/server/state scope | Existing host/provider badges, environment label, unavailable filter choices and per-scope data | Mandatory real F1 pins GitHub to avoid masking. Other scope labels unchanged; no cross-product capture requirement. |
| Default ready/blocked/updated versus explicit date/size sort; stats loaded/missing | Text search preserves helper order for default sorts; explicit date/size sorts reorder; stats enrich rows | F2 uses default sort to make append order assessable. No new loading, stats, or sort styling. |
| List panel closed/open/maximized; selected row; right-panel presence animation; tab active/inactive | Existing selection, detail panel, status seeds and tab controls consume the new rows | F2 selects a supplemental row then closes panel. No separate panel-size matrix. |
| Light/dark; narrow/wide; Electron/web; scrolled condensed header | Existing styling, responsive container and titlebar rules unchanged; normal and condensed search share the same query callbacks | Single dark, wide web capture family. Desktop equivalent; no mobile run. The condensed search, Cmd/Ctrl+F, typing, paste and Enter all reach the same state. |

Interactions reachable on the changed page were traced: search typing/clear/Enter/paste and Cmd/Ctrl+F; normal/condensed search focus; state/involvement/host/server/project and author/label/draft/review/checks menus; sort; refresh/retry/check-again; load more; row selection; detail-panel open/close/maximize; tab activate/close/close-other/close-right/reorder; copy-reference shortcut; speed actions and close sweeps, their optimistic override/rollback and refresh. The diff adds no new handler or action. F1–F6 cover query/filter transitions; F2 covers selecting the newly included row; F3 covers the reverse route back to the unfiltered list. Destructive PR actions are not needed for this proof and must not be exercised on public PRs.

Source locations in the audited worktree:

- `apps/web/src/components/pullRequest/PullRequestsPage.tsx`: baseline 336–353; partition gate 377; carried/answered data 537–548; merge caller 686–705; search 1096–1102; render branch order 1141–1224; list/panel props 1308–1355, 1451 onward.
- `apps/web/src/components/pullRequest/pullRequestListGroups.logic.ts`: narrowing/union 58–90; statuses and grouping/maps 93–138.
- `apps/web/src/components/pullRequest/usePullRequestListView.ts`: query debounce/filter merge 184–219; display sorting/counts 266–321.
- `packages/client-runtime/src/pullRequestQuery.ts`: checks/status aliases; `pullRequestList.logic.ts`: substring predicate, local filters, identity, grouping and sorting.

## Fixture matrix

All required captures: **web, 1440 × 900 viewport, dark theme, default sort, isolated development state**. Record actual viewport and theme in the capture manifest. A desktop pass is optional because its list uses this same page. No second width/theme is justified by the diff. All statuses below are planning status, not test passes.

| ID / named fixture | Data needed and distinct rendered state | Diff affected? / expected result | Seed / drive path | Minimum evidence | Status |
| --- | --- | --- | --- | --- | --- |
| **F1 — warm baseline, host misses partial word** | Baseline includes at least one title containing Toast and one containing Cursor; host answers for partial query are empty. Start with loaded rows, then type `toa`, extend to `toas`, replace with `Curs`. | **Yes, core fix.** Same matching identities survive typing, debounce and completed host reply; no settled “Nothing matches”. One-row result and its group count remain correct. | **Real app required.** Use the lead's isolated project backed by a bare initialized git directory with origin [pingdotgg/t3code](https://github.com/pingdotgg/t3code). Open `/pull-requests?involvement=all&state=open&host=github.com`. Wait for the text-free baseline, confirm the actual matching rows are in its first 99, then type. The lead observed [#26908](https://github.com/pingdotgg/t3code/pull/26908) for Cursor; revalidate its current state and baseline membership. If public data has moved, select another live partial-word example and record the identities/query; do not silently count an unrelated host hit as proof. | **recording, before AND after** at the same viewport/theme/data. Show query edits, local result and completed host answer. Record RPC/request completion alongside the video; do not infer completion solely from the spinner. | **PENDING CAPTURE — required real app** |
| **F2 — mixed answer, overlap, body-only host hit and multiple supplements** | One group initially suffices: viewer bilal, other-author rows. Host H1 title Toast, H2 title Notifications (only body contains query); baseline repeats H1 plus B3 Toast fallback, B4 Toaster, and one nonmatch. Distinct timestamps determine supplement order. Four final identities. | **Yes.** Keep H1/H2, including H2's matched-elsewhere indicator; show H1 only once; append ranked B3/B4 within their group; group count is 4. Selecting B3 opens its correct detail and close returns to the same results. | Live public host data cannot be forced into this exact body-hit/overlap combination by a route or read RPC. **Labelled harness capture** is acceptable here: capturer supplies controlled baseline and query answers at the page's `usePullRequestList`/`pullRequestEnvironment.list` boundary, preserving the actual page, debounce, grouping and row rendering. Baseline answer has no query, query answer has H1/H2, `searchesOnHost: true`, same scope/identities. Also supply B3's detail response for the selection step. No harness was built by this audit. A verified equivalent live dataset may replace it. | **recording, after**: type query, resolve host answer, show ordered identities/count, select B3, close detail. Pin default sort. | **PENDING CAPTURE — required; harness unless live equivalent is demonstrated** |
| **F3 — genuine zero matches, then clear restores baseline** | Nonempty baseline; both local predicate and host answer return zero for an implausible token such as `zz26950nomatchzz`. Clear to blank query; baseline has at least one nonempty group. | **Control / changed data path.** Show search/loading phase if present, then true settled “Nothing matches”; Clear search restores original rows and group counts. Old priority partitions must not leak unrelated rows into text search. | **Real app.** Same route/project as F1; type the sentinel, await completed host answer, use Clear search. Confirm the sentinel has no baseline match and host actually returned zero. | **recording, after** from loaded baseline through empty state and clear/recovery. | **PENDING CAPTURE — required real app** |
| **F4 — role-scoped supplement obeys local qualifiers** | Known viewer bilal. Baseline includes an eligible authored PR with Toast in title plus text-matching negative controls: other author, draft, wrong review decision, missing required label, excluded label. Query can combine `toa author:bilal draft:false review:approved label:bug -label:wip`; eligible row is non-draft, approved, bug-labelled, not wip. Host query returns zero. | **Yes.** Only eligible baseline row remains; no excluded controls leak in. `involvement=authored` uses an unlabelled list rather than all-involvement group headers. Reviewing is the equivalent layout with `viewerReviewRequested` eligibility; not a separate required capture. | Real app may use an author/qualifier present in its actual baseline, but exact negative-control data is not controllable read-only. **Labelled harness capture** with controlled list answers/viewer is the deterministic fallback, route `involvement=authored&state=open&host=github.com`, preserving actual parser and local narrowing. Supply the role-scoped baseline as the server would; exercise the author/involvement negative branch separately as logic evidence if the fixture bypasses server filtering. No need for a separate video per qualifier. | **recording, after**: start from all, switch to authored and type qualifiers; after host zero settles, only eligible row remains. Manifest lists negative-control identities and why excluded. | **PENDING CAPTURE — required; live or labelled harness** |
| **F5 — typed checks/status veto** | Baseline has a locally matching Toast row not eligible for passing checks; host answer for qualified partial query is zero. | **Yes, new caller gate.** At settled `toa checks:passing`, baseline must not restore that row. Repeat with `toa status:success` to cover alias; both remain empty after answer. Remove typed qualifier and partial match returns. Transient carried rows are existing behavior; assess veto after host answer. | **Real app if suitable checks data exists**, same F1 route. Otherwise **labelled page harness**: baseline unqualified with matching failing/unknown-check row; qualified host answer empty; run real parser/caller, not just `pullRequestGroups(baseline: undefined)`. | **recording, after**, both aliases plus removal in one clip; completed host replies identifiable. | **PENDING CAPTURE — required; live or labelled harness** |
| **F6 — menu checks keeps eligible baseline supplement** | Menu `checks=passing` baseline contains one passing Toast match and no failing control; text query `toa` has no typed checks/status; host text answer zero. | **Yes, complementary gate branch.** Matching passing row persists; menu filter stays selected. Typed-check veto from F5 must not be incorrectly applied to all checks filters. | Route `/pull-requests?involvement=all&state=open&host=github.com&checks=passing`, or select Checks → Passing in app. Confirm baseline RPC actually used passing filter. Use live data when available, otherwise **labelled page harness** returning a checks-filtered baseline and empty text reply. A failing row belongs in the unfiltered control data, not in the checks-filtered server baseline. | **recording, after**: set menu, await baseline, type partial query, show retained row after host completes. | **PENDING CAPTURE — required; live or labelled harness** |

## Capture handoff and acceptance

Minimum set is six after recordings (F1–F6) plus an F1 before recording, all at the one viewport/theme. One continuous after recording can cover multiple rows if it has explicit chapter times, row identities, and response-completion evidence. Extracted stills may illustrate the result but do not replace recordings. Label each before/after and every harness segment visibly and in the manifest.

The capturer must use the existing isolated dev environment or a separately authorized one. Do not point it at live T3 userdata. Confirm the requested head before capture. Keep `host=github.com` selected: local GitCafe substring rows can mask the failure. Warm the text-free baseline before F1. Do not navigate directly to a query URL and claim it proves the debounce sequence. A long enough still after typing does not prove host completion; preserve sanitized request/response metadata (query, relevant filters, completion, identity lists), without credentials, cookies, startup/pairing URLs or private payloads.

No existing app setting or writable RPC can create exact public forge search responses. Deterministic cases above are expressly harness captures and must not replace the real-app core F1. Build them outside the worktree if the lead authorizes that capture work. This audit only derives the fixture requirements; it does not assert a harness already exists.

Unchanged lifecycle variants (cold/absent baseline, unsupported capability, error, pagination, alternate sort, responsive width, theme, desktop shell, destructive actions) were traced and collapsed above. Expand the minimum only if a capture reveals a difference in one of those paths or a reviewer establishes a new failure. This keeps evidence proportional to a list-membership fix.

Coverage of this pass: complete supplied diff; sole production callers; parser/filter eligibility; grouping/counts/empty states; downstream selection/status/stats paths; relevant conditional render branches and interactions. No browser, server, test runner, edits, pushes or comments. Only this scratch artifact and its explicitly requested public upload were created.

FRICTION: tool | Two source lookups assumed export-shaped file paths; actual files use camelCase. Corrected using rg --files; no coverage lost. | Resolve package export mappings before guessing source paths.

VERDICT: SUFFICIENT
