# Proof audit, pass 1 — welcome import copy

Findings: no source-level defect found in the three copy changes. The issue asks for wording that includes all supported sources or names none; the head names none. No captures have been reviewed or produced in this pass. SUFFICIENT below means a complete, capturable plan, not visual acceptance or merge approval.

Issue: [#33252](https://git.cafe/pingdotgg/t3code/issues/33252). Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code).
Reviewed branch: `fix/issue-33252`; head [21e22d030c](https://git.cafe/pingdotgg/t3code/commit/21e22d030ca430ef680082630274b3b1c073d35c); supplied base and head's direct parent [c6ca605630](https://git.cafe/pingdotgg/t3code/commit/c6ca6056309904f89b90b6055d797973be0a0939). Complete diff: one file, three changed strings, no behavior/style changes. Local `main` is older than the supplied base; this audit uses the explicitly supplied base, not that stale local ref.

## Render-path inventory

- Production caller chain: `/welcome` route → `WelcomeRouteView` → `WelcomeWizard` → one `ImportStep`. `/welcome#agents:<environmentId>` starts at Agents instead of Connect, then reaches the same ImportStep. Authentication normally redirects unauthenticated visits to `/pair`; hosted-static is also accepted. Automatic first-run navigation reaches that same route. Returning from provider login can use the agents hash. No other production ImportStep or WelcomeWizard caller was found. The existing import browser test mounts WelcomeWizard directly as a harness.
- `WelcomeWizard.localAvailable` affects the earlier connection UI; `resumeEnvironmentId` seeds setup IDs and initial stage; `onDone` dismisses/navigates or reports completion failure. None selects different import copy. Web and Electron share this component; native mobile has a different onboarding flow and does not render these strings. Local/direct, remote, hosted web and relay/tunnel use the same import UI. Selected environment count and results, rather than transport, distinguish its layouts. No new RPC, agent tool, keybinding, command-palette or Settings entry is introduced.
- `ImportStep` props: `scans` controls all scan content; `importSelection` is null/default or explicit environment IDs plus candidate keys; `isImporting` disables controls and changes the primary button label. `onImportSelectionChange`, `setIsImporting`, and `onDone` drive selection, progress and departure. `useEnvironments` supplies computer labels (fallback `Computer`); orchestration-operate grants control import availability and its permission error.
- Loading copy appears exactly when **every** scan has `data === null` and **some** scan is pending. It has heading `Your projects`, a large spinner, centered text and the skip button. This global branch masks even another scan's error while all data are null. Parent WizardPanel holds the preceding Agents panel height during this branch; the transition and spinner require recording.
- Otherwise the changed StepShell description always appears under `Import your projects`. The only other StepShell caller is AgentsStep (`Connect your agents`), whose description is unchanged. StepShell renders an optional description only when truthy; ImportStep always supplies a nonempty one, so absent-description is not a fixture for this diff.
- Candidate total > 0 adds `selected of total`, Select all and Select none. Zero candidates omits the selection bar. Selection 0, 1, partial, all controls enable/disable states and `Import 1 project` versus plural. Defaults select git/legacy-git candidates with at least 3 threads, non-null activity from the past 30 days and not in the future. `git: null`, <3 threads, old/null/future activity default unchecked. Explicit keys override defaults for scanned computers; later computers get defaults.
- A fieldset is rendered for each scan; one computer hides the legend, multiple show the environment label. Per-scan precedence: pending with null data → small spinner/unchanged `Looking for projects…`; else error → alert/Retry; else zero candidates → changed `No existing projects found.`; else no status paragraph. With cached data, pending does not hide results or show a spinner; an error can coexist with cached candidate rows. `data.truncated` independently adds the scan-limit warning beside empty, populated or cached-error content. A zero-length scans array renders description/footer, but no fieldsets and no empty-state paragraph. Null-data/nonpending/no-error with one scan has the same layout as a successful empty scan.
- The list uses ScrollArea (`max-h-80`) inside the outer wizard scroll area. Repository identity uses `remoteKey` when present, otherwise path; repository label is git.repository or candidate.title. A one-candidate group is a direct row; multiple clones are an initially open collapsible group, with whole-group checkbox checked/indeterminate/unchecked. `git: null` candidates form initially closed Other folders, with singular/plural folder count and the same three checkbox states. Optional missing git (older server) is treated as a repository.
- Rows can be nested or direct, with or without a repository label. Path parent may be absent or truncated; final basename is preserved as space permits. Windows drive/UNC roots are handled by path splitting, without a separate platform branch. Hover/focus tooltip shows full path. Source icon slots are fixed for Claude Code, Codex and Pi; nested rows suppress icons while the group aggregates them. Thread count is numeric (0/1/many do not select a textual branch); null activity shows a blank age, current activity `now`, older activity a relative value. `projectId` and `alreadyImported` do not change row markup, but projectId affects import execution/reuse.
- Footer: importing disables fieldsets, Select all/none, skip, import, and stage navigation; primary text becomes `Importing…`. Missing operate grant for any selected candidate shows the permission message and disables import. With zero selected, every() is true and that error disappears. A grant revoked during import can set the same error; restoration hides that stored permission error. No other importError assignment exists.
- Interactions: initial Continue → Continue (or agents-hash → Continue); direct and group checkboxes; Select all/none; expand/collapse repository/Other folders; scroll; row tooltip by hover/focus; back to Connect/Agents and Continue preserving selection; add another computer; Retry; Import; Do not import projects; Close and Escape. Outside pointer dismissal is disabled. Close/Escape finish only when not importing. Import can finish to a project or home, show success/partial-history warning, or leave the wizard open with a completion-error toast when settings cannot save. These completion messages are unchanged and occur after/around leaving the changed copy. No additional import-copy variants are generated by import counts or warnings.
- Styling inputs: description is left aligned, `text-sm leading-relaxed`; empty status is left aligned `text-sm py-2`; loader copy is centered `text-sm`. All use muted foreground. Wizard is wide (`max-w-3xl`), viewport padding 16px, panel horizontal padding 24px, centered even on small screens (`bottomStickOnMobile=false`). Below the small breakpoint, stage labels hide; footer wraps. Light/dark change theme tokens, not branches in ImportStep. Interface font size is adjustable 12–20px (default 16); system/platform font metrics and custom fonts can affect wrapping. Reduced motion disables spinner/height animation without changing copy. Height changes animate; no screenshot alone establishes transition behavior.

## Fixture data and seeding contracts

`S(env, candidates, truncated=false)` means `{environmentId: env, data:{candidates, scannedAt: ISO-now, truncated}, isPending:false, error:null, refresh}`. `P(env)` means null data, pending true, null error. `F(env)` means null data, pending false, error `Synthetic scan unavailable`. A cached variant retains S.data while setting pending or error. All normal candidates have existing absolute path, title, sources, threadCount, lastActiveAt, alreadyImported:false, and git metadata. Use dates relative to capture time, comfortably away from the 30-day threshold.

`Pi3`: one real synthetic git repository, sources `[pi]`, threadCount 3, lastActiveAt one day ago or current lead-created sessions, git `{remoteKey:null, repository:null}`. Default checked. Use the lead's already-created real Pi sessions; do not run more paid agent turns merely for copy evidence. An initial stable page with dated fixture data is a still; a row that verifies time progression, loading or an interaction is explicitly a recording below.

**Real app recipe R:** use an isolated dev server/state and the lead's scratch Pi session directory. Enable Pi via Settings → Providers → Add provider → Pi; point its provider environment at `PI_CODING_AGENT_SESSION_DIR` if necessary. Keep Claude/Codex provider homes isolated and empty (instance-specific home overrides take precedence over host `CLAUDE_CONFIG_DIR`/`CODEX_HOME`). Open `/welcome`, select the intended connected computer(s), Continue → Continue. For empty, change the scratch Pi session directory to an empty directory or hide its fixture sessions; do not delete evidence. Reopen only after a fresh scan is guaranteed: scan-query staleTime is 30 seconds and cache idle TTL is five minutes, so capture with a fresh client/query registry or use an explicitly controlled refresh, not a quick back/forward assumed to rescan. Reuse the lead-owned server only with coordination; this auditor starts none.

**Labelled harness recipe H:** deterministic pending/error/cached/truncation/permission/completion cases have no stock fixture RPC or query-string switch. Use the real WelcomeWizard and production CSS/atoms in a separate capture harness; control the `useProjectScans` return values above, environment registry labels/grants, and command completions, following the existing import browser test's mock boundaries. Keep harness files and outputs in the capturer's own scratch area. Drive real UI handlers and release explicit promise gates, not arbitrary sleeps. This is a harness capture, not evidence of real scanner/provider discovery. The existing test's static scan mock alone does not implement pending or per-computer result changes; extend that boundary in the scratch harness, without editing the review checkout. A web browser capture harness is sufficient; no native-mobile harness is relevant.

## Fixture matrix

All statuses are planning statuses: **READY-R** = real app seed described, **READY-H** = labelled harness recipe described, **READY-R/H** = real seed with deterministic harness fallback. Nothing below claims CAPTURED. Context rows enumerate distinct unchanged surroundings and are not added to the minimum proof set for a three-string change. Independent dimensions are combined in one scene or recording rather than multiplied into every possible cross-product.

| ID / named fixture | Data and distinct visible state | Diff affected | Evidence kind | Seed / driver | Status / minimum |
|---|---|---|---|---|---|
| L — cold scan → Pi ready | P(A) → S(A,[Pi3]); loading heading/spinner/new loader copy → description and checked single repo. Also cover P(A)+F(B): global loader still masks B while no scan has data. | Loader, then description | **recording**; show entry from Agents and settled endpoint | R for normal scan; H with a release gate if real loading cannot be read reliably; label H if used. | READY-R/H; **required** ordinary P(A) → Pi3, masking variant context only |
| P — one Pi repository | S(A,[Pi3]), 1 of 1 selected, no computer legend, Pi badge, `Import 1 project`. Fixed date, stable endpoint. | Description | still | R using existing lead sessions; no actual import needed | READY-R; **required** real-app endpoint |
| E — no sessions | S(A,[]), no selection toolbar, new empty text, disabled `Import 0 projects`, skip enabled. Null-data/nonpending/no-error is visually equivalent. | Description + empty text | still | R with empty isolated homes and Pi sessions hidden; fresh scan required | READY-R; **required** |
| M — mixed computers | S(A,[Pi3])+S(B,[])+P(C), descriptive long computer labels; 1 of 1 selected; A results, B new empty text, C unchanged inline spinner. Release C to nonempty or empty. Missing environment label uses `Computer`. | Description + B empty | **recording** | Real app: three paired isolated servers with separate histories; reliable pending/label-fallback variant uses H | READY-R/H; context |
| F — failed scan, Retry | F(A), then click Retry: P(A) → S(A,[]). No empty text in error state; loading after Retry; final empty text. Multiple all-failed scans add legends, same branch. | Description in error, loader during retry, empty after | **recording** | H gate returns synthetic scan failure then success; real failure injection lacks a supported seed switch | READY-H; context |
| C — cached refresh/error | S(A,[Pi3]) → cached pending → cached failure plus candidates → Retry → success. Cached pending has no spinner; cached failure adds alert without removing list; optional truncated=true adds warning. Cached empty failure omits empty text in favor of alert. | Description; empty only after successful empty | **recording** | H retains the exact cached data while toggling pending/error; include empty cache as alternate scene | READY-H; context |
| T — truncated results and empty | S(A,[Pi3],true)+S(B,[],true). Both computer sections show limit warning; B also shows changed empty text. | Description + empty | still | H. Real scanner limits can produce this but manufacturing a large corpus is unnecessary and not deterministic enough for proof | READY-H; context |
| G — groups, counts, selection, overflow | One scan with two clones of one remote group, one singleton repo with absent legacy git metadata, one no-remote repo, and two nongit folders. Add ≥15 rows to overflow. Mix Pi/Claude/Codex source slots; ages null/now/older; thread counts 0/1/3/large; long labels/paths plus basename-only and Windows-root path variants. Start 0 selected, check one, partially check a group, select all, select none; expand/collapse both group types, hover/focus tooltip, scroll. Other-folders 1-folder variant is a separate scene in the same recording. | Description; surrounding rows unchanged | **recording** | R possible using cloned synthetic repos/sessions and nongit dirs; H for legacy-missing-git, empty sources/numeric extremes and cross-platform path samples. Label those scenes H. | READY-R/H; context |
| A — default-selection ages/counts | S(A,[recent git 3 threads, recent git 2, old git, null-age git, future-age git, nongit 3]); only first defaults checked. Verify stable mixed selection, then back/Continue retaining explicit selection; add B and return to show B defaults. For a true threshold/time-progression check advance the harness clock and rerender, do not wait days. | Description; defaults unchanged | **recording** (navigation/time-dependent variants) | R for ordinary recent/low-count/old seeds; H for null/future/threshold and deterministic added computer | READY-R/H; context |
| D — permission denied/restored | S(A,[Pi3]); selected but no operate grant → permission message/import disabled; Select none → no permission error; select again, restore grant → enabled. Also gate create, revoke grant, release create, restore and retry to cover stored permission error. | Description | **recording** | Real read-only connection possible; H for deterministic grant changes and mid-import denial, using session atoms like existing browser test | READY-R/H; context |
| I — importing → finish | S(A,[Pi3]) and a selected multiple-project variant; click import, hold command promise to show disabled controls/`Importing…`; attempt back/close, release to success. Skip and Close/Escape in a fresh nonimporting scene finish without importing. Outside click leaves modal open. | Description while visible | **recording** | R on disposable state; H gates create/import/waitForProject for readable progress. Real import is outside required copy proof. | READY-R/H; context |
| O — completion outcomes | Selected import finishes to project/home; variants return skipped history, failed command, missing client project, or fail completeOnboarding once then retry. Success/warning toast after departure; save failure leaves wizard and error toast. Skip/Close can also hit save failure. | Description only while wizard remains; toasts unchanged | **recording** | H using existing test's deferred command/waitForProject/completeOnboarding boundaries. Normal success can be R. | READY-R/H; context |
| Z — no setup environments | scans=[]; no global loader, no selection bar, no fieldsets/empty text; description/footer remain. | Description | still | H only for this inventory: ordinary Connect refuses an empty setup, and a valid resumed environment normally yields one scan | READY-H; context, unreachable via normal empty Connect |
| W — narrow/font/motion stress | E and P at narrow width; set interface font 20px and system font; optionally resize through 640px and use reduced motion. Check wrapped description/empty text, footer wrap, nested/outer scroll access. At 12px strings only contract, so no independent minimum. | All copy where present | **recording** for resize/settings/motion sequences; stable 20px endpoint can be still | R via Appearance/Typography settings and viewport/media configuration; H for controlled media/time sequences | READY-R/H; context beyond the ordinary narrow minimum |

## Minimum capture set and acceptance criteria

Use the same fixture inputs, font, browser/client, framing, zoom and clock on base and head. Label every artifact Before / After with its revision and whether real app or harness. Capture the whole dialog, including heading, description, list and footer. Avoid tokens, real user content and account metadata in frame or uploads.

| Viewport × theme × row | Required capture per revision | Purpose |
|---|---|---|
| 1280×900 CSS px × built-in dark × L → P | **recording**, plus settled P still | Read loader copy, see height transition and real Pi result under corrected description |
| 390×844 CSS px × built-in light × L → P | **recording**, plus settled P still | Narrow wrapping and transition; distinct small-screen step header/footer layout |
| 1280×900 CSS px × built-in dark × E | still | Wide description and empty copy |
| 390×844 CSS px × built-in light × E | still | Narrow description and empty copy |

This is 2 recordings and 4 stills per revision (4 recordings and 8 stills total). L and P share one sequence; do not create an extra loading still as its sole proof. P and E must use real-app seeds; when loading is too fleeting, L may be an explicitly labelled production-component harness recording with the same viewport/theme and P real-app still alongside it. Each of the three changed strings is represented at wide and narrow widths, with both light and dark represented. Because this diff changes neither theme tokens nor responsive styles, the wide-light/narrow-dark cross pairs, exhaustive custom fonts/themes, native OS combinations, and context-only matrix rows are not minimum captures. They can be added if an endpoint exposes clipping or contrast concerns.

Required wording on head:
- Loading: `Looking for projects from your agents…`
- Description: `Import projects and conversations from your agents on your selected computers.`
- Empty: `No existing projects found.`

Base must visibly show the previous Claude Code/Codex-only strings. Pi-only P should visibly include its Pi icon and fixture project. E must be a settled successful empty scan, not an error, unresolved scan or unverified stale cache. Recordings must show the full changed loading sentence clearly and its transition, without relying on an artificial delay being represented as real scan performance. Compare text wrapping, clipping, scroll access, footer reachability and height behavior. No additional copy test or source-text assertion is required for this cosmetic fix.

## Audit boundary

Code and issue inspection only. No browser, server, tests, worktree edits, forge comments, pushes or PR creation. No server was started, so none needs stopping. No fixtures/captures or harness have been executed by this auditor; the READY statuses describe capturability only. Available disk at audit checks: 9 GB, above the 5 GB stop threshold. All generated auditor artifacts stay under `/tmp/issue-lead-33252-r2/proof-audit-1/`.

FRICTION: none

VERDICT: SUFFICIENT
