# Proof audit, pass 1

## Findings

1. **Pinned-head defect: the `_full` clone route loses the repository name.** Two `my-repo` checkouts with `https://dev.azure.com/example-org/Platform/_git/_full/my-repo` produce `Platform/_full`, rather than `Platform/my-repo`. Microsoft documents `_git/_full/Repo` as a valid clone/fetch URL form in [Limited Refs](https://learn.microsoft.com/en-us/previous-versions/azure/devops/all/git/limited-refs?view=azure-devops-2020). The parser takes the first segment after `_git` without checking the remaining path. `_optimized` similarly becomes the displayed repository segment; extra tail segments are ignored. F14 makes this visible and seedable. This is an implementation finding; the pass-1 verdict below evaluates matrix completeness, not whether the implementation passes its rows.
2. **The single-checkout exception matters.** `buildProjectGroups` calls the changed helper only for more than one *physical* member, but mobile Settings calls it directly in its index and project overview. A single Azure checkout, including one in “Keep separate” mode, therefore changes in those two screens. Its Home filter and Settings filter menu still use the physical project title. A grouped-only web capture misses this path.
3. **The affected surface list is larger than the sidebar filter.** It includes web/desktop palette project results and actions, draft-picker options, sidebar project metadata and hover cards, folder headers, Settings project choices and name inputs, the image-file picker’s group title, removal confirmations for multiple entries, setting-inheritance project links, the Voice project-dictionary description, and archived-thread headings when archived threads span multiple environments. Mobile additionally has native filter menus/chips, New Task project rows, Settings scope menus, and project override sections. Mobile draft/queued cards receive the label only as an icon-fallback input, not as visible project-name text; it can change the fallback glyph/color.
4. **The grouping and title rules must remain visible in the matrix.** Uniform custom titles win. Divergent titles use the shared repository label. HTTPS and SSH can now agree after decoding; remotes that differ in project/repository case still produce multiple display labels and fall back to a shared repository name. A fork uses its own `origin`, even when `upstream` is Azure and `origin` belongs to another forge. Missing `origin.remoteUrl` must use the origin display/canonical fallback, never the upstream label.
5. **Some shapes need an explicitly labelled harness.** A single isolated server cannot supply two environment IDs. Optional identity fields absent from current discovery and invalid/empty titles also cannot be manufactured through project-add. Those rows have concrete harness inputs below; they are not claimed as real-app captures.
6. **Pass 1 is complete; visual proof is still pending.** This audit derives the matrix and seeding procedure. No browser, simulator, server, or capture was started. No PR was opened, and no checkout file was changed by this audit. The existing shared tests cover the principal parser/grouping behavior, but they cannot demonstrate native wrapping, menus, or the mobile single-checkout callers. A mobile row is needed.

Scope: issue [#33607](https://git.cafe/pingdotgg/t3code/issues/33607), also mirrored as [#33607](https://github.com/pingdotgg/t3code/issues/33607), branch `fix/issue-33607`, head [245a3b48f5](https://git.cafe/pingdotgg/t3code/commit/245a3b48f54247d58d2259cc58211b7e660ff946), compared with the supplied base [ee59ab22a8](https://git.cafe/pingdotgg/t3code/commit/ee59ab22a819acb0544d85cd6cca0e4aa8c7c444). The local `main` ref has advanced, so it was not used to redefine this scope. Five files change in the pinned diff: grouping helper/tests, Azure definition/tests, and the shared definition registry. No rendering component or stylesheet changes. The worktree began clean but acquired concurrent edits in four source/test files during this audit; those edits were not made or reviewed by this audit. The final evaluation reloaded the grouping and Azure parser bodies from the pinned git commit into scratch, leaving the checkout untouched.

Status vocabulary: **SEEDABLE** = a real-app recipe is specified, not yet captured; **HARNESS** = synthetic harness inputs are specified and any capture must display that label; **EVALUATED** = a read-only synthetic evaluation checked the pinned head function and the base helper. “Affected” below means the label output or its use changes, not that the renderer was edited.

## Named data fixtures

All names, repositories, paths, IDs, and timestamps below are synthetic. Unless specified otherwise: two different checkout directories, one environment, repository grouping, matching canonical repository keys, titles equal to the repository name, no pin/archive/icon override, identity discovery finished. Two database records at the same environment/path do **not** count as two physical members.

| Row | Fixture and necessary data | Before → after / affected branch | Minimum visual proof | Status |
| --- | --- | --- | --- | --- |
| F01 | **Azure HTTPS, ordinary group.** Two `my-repo` folders with origin `https://dev.azure.com/example-org/My%20Project/_git/my-repo`; both titles `my-repo`. Also use this output for divergent titles `Checkout A` / `Checkout B`. | `example-org/My%20Project/_git/my-repo` → `My Project/my-repo`. **Affected.** Shared repository label wins. | C1; use as the common label throughout, not a separate capture at every caller. | SEEDABLE; EVALUATED |
| F02 | **Azure SSH and legacy spellings.** Two `ssh-repo` folders with origin `git@ssh.dev.azure.com:v3/Example-Org/Other%20Project/ssh-repo`. Parser-equivalent variants: `ssh://git@ssh.dev.azure.com/v3/Example-Org/Other%20Project/ssh-repo.git`, `Example-Org@vs-ssh.visualstudio.com:v3/Example-Org/Other%20Project/ssh-repo`, and `https://example-org.visualstudio.com/DefaultCollection/Other%20Project/_git/ssh-repo.git`. | Canonical/raw organization/route text → `Other Project/ssh-repo`. **Affected.** Decode, preserve remote case, strip `.git`, web `_git` / SSH `v3` branches. Each variant needs the same two physical checkouts; mixed aliases only group when their existing canonical keys agree. | SSH beside F01 in C1. Legacy/URI variants are parser-equivalent and need no repeated visual capture. | SEEDABLE; parser variants EVALUATED |
| F03 | **Custom title and title precedence.** Start F01, rename both members to `Team Console`, then restore both to `my-repo`. To coexist with F01 use a distinct remote `https://dev.azure.com/example-org/Custom/_git/custom-repo`, folder `custom-repo`, title `Team Console`. | `Team Console` → `Team Console`: **unchanged control**. A title equal to `My Project/my-repo` is also unchanged. A persisted title equal to the old full raw display path stays that old string after the fix because it now qualifies as custom; do not expect it to be rewritten. | Rename/restore in C1, **recording**. No extra still for each equivalent title branch. Divergent-title branch is F01. | SEEDABLE; EVALUATED |
| F04 | **One repository, mixed transports/case.** F01’s first checkout uses HTTPS; second uses `git@ssh.dev.azure.com:v3/Example-Org/My%20Project/my-repo`. Variant: second project segment is lowercase `my%20project`. | Matching case: `my-repo` → `My Project/my-repo`, **affected** and visually equivalent to F01. Different case: `my-repo` → `my-repo`, **unchanged fallback**, because the helper compares display labels case-sensitively. | Matching-case output reuses C1/F01. Code transcript is sufficient for the unchanged case fallback; do not mistake it for a failed capture of F01. | SEEDABLE as its own seed phase; EVALUATED |
| F05 | **Fork identity.** Two `fork-repo` folders, origin `git@ssh.dev.azure.com:v3/Example-Org/Forks/fork-repo`, upstream `https://dev.azure.com/example-org/Platform/_git/fork-repo`, titles `fork-repo`. Discovery chooses upstream as locator and saves distinct origin identity. | `example-org/forks/_git/fork-repo` → `Forks/fork-repo`. **Affected.** Must not show `Platform/fork-repo`. A second, different origin must remain a separate group. A non-Azure origin keeps its own forge’s fallback label. | F05 beside F01/F02 in C1; confirm selecting it opens its own Settings name. | SEEDABLE; Azure-origin case EVALUATED |
| F06 | **Long decoded label / encoded repository.** Two `customer-operations-configuration-console` folders, origin `https://dev.azure.com/example-org/Enterprise%20Platform%20Administration%20and%20Developer%20Experience/_git/customer-operations-configuration-console`. Short parser variant: `.../My%20Project/_git/My%20Repo.git`, folder and title literally `My%20Repo`. | Long raw route → `Enterprise Platform Administration and Developer Experience/customer-operations-configuration-console`; variant → `My Project/My Repo`. **Affected.** The encoded-folder spelling intentionally makes the title equal the identity’s repository name; a uniform decoded custom title follows F03 instead. Bad `%ZZ` decoding returns the original segment without throwing. | C2 narrow web and C6 Android: truncation, two-line clamp, input scrolling, and native row wrapping. | SEEDABLE; EVALUATED |
| F07 | **Other forge / unrecognized or unreadable remote controls.** Two `t3demo` folders with the [synthetic GitHub remote](https://github.com/ExampleOwner/t3demo.git). Additional controls: `https://forge.example.test/example-org/Project/_git/other-repo` and `https://dev.azure.com/example-org/legacy/my-repo` (no `_git` or `v3`). | GitHub stays `ExampleOwner/t3demo`; unknown host stays its old repository path; recognized Azure with an unreadable label path falls back to its old display name. **Unchanged.** GitCafe, GitLab, Forgejo, Bitbucket, Cursor Origin, and unknown have no new label hook. | GitHub beside affected rows in C1. The other hook-null controls need code checks, not repeated screenshots. | SEEDABLE; representative controls EVALUATED |
| F08 | **Single checkout / grouping disabled / repository path.** One `solo-repo` with `https://dev.azure.com/example-org/Solo%20Project/_git/solo-repo`; or F01 with `separate` globally or per-checkout. Repository-path variants: two repo roots, equal relative subpaths, and different relative subpaths; explicitly set subpath titles to `my-repo` (or different titles) to avoid the uniform-custom-title branch. | Normal group builders keep each singleton’s physical title, **unchanged**; matching root/subpath groups with >1 member change like F01. **Mobile Settings index/overview are affected even with one member:** old raw display → `Solo Project/solo-repo`, while its menu/Home entry stays `solo-repo`. | C5 must show menu, index value, and overview together for the singleton. C1 records grouping off/on; path variants reuse the same label output. | SEEDABLE; singleton/separate/root-path EVALUATED |
| F09 | **Origin fallback and missing metadata.** Harness identity has Azure upstream locator, origin `{canonicalKey: "dev.azure.com/example-org/forks/_git/fork-repo", displayName: "example-org/Forks/_git/fork-repo"}` and **no origin remoteUrl**; variant omits origin displayName too. Two members titled `fork-repo`. | Origin display path, or origin canonical key when displayName absent, stays unchanged. **Unchanged.** An Azure upstream must not override the origin. Current discovery always supplies origin remoteUrl, and project-add cannot inject this shape. | Labelled diagnostic harness / synthetic evaluation; no mandatory visual capture because this is unchanged text fallback. | HARNESS; missing-origin-remote case EVALUATED |
| F10 | **Last-resort/empty branches.** Harness variants: identity null; no displayName/name with unrecognized or local remote; distinct nonempty titles and distinct repository names; identical custom titles with surrounding whitespace; empty/blank member titles; empty members with a nonempty representative title. Keep a common canonical key only where grouping is needed. | Unique custom title → shared label → shared repository name → representative title. Empty values are ignored. Empty titles with a shared Azure label change like F01; no label/name returns the representative. Empty/invalid titles are not app-seedable through the wire schema. Empty input projects produces no groups. | Code/harness cases; F01/F03/F04 already cover every distinct visible output class. No separate empty-state screenshot obligation introduced by this diff. | HARNESS; representative and empty-title branches EVALUATED |
| F11 | **Duplicate display labels / Settings count boundary.** Add two members of `https://dev.azure.com/another-org/My%20Project/_git/my-repo` alongside F01; keys differ, both now show `My Project/my-repo`. Fill to >6 project groups with distinct synthetic remotes. | **Affected collision:** omission of organization makes labels identical. Settings must retain machine/path disambiguation; project keys, selection and membership remain distinct. `options.length <= 6` uses a radio menu; `>6` uses searchable combobox. | C2, **recording**, both count branches and selecting the two distinct keys; C4 covers multi-environment secondary labels. | SEEDABLE |
| F12 | **Pinned / archived group.** Pin and then archive all members of F02 using project Settings; partial pin/archive is a harness variant (`some` pins the group, `every` archives it). | Label changes as F02; pin adds/reorders the row, archive removes picker entries but preserves Settings access. Native Settings adds ` · Archived` to menu items. Partial archive leaves group visible. **Affected text in existing states.** | C1 pin/unpin and archive/unarchive, C2 Settings archived choice, C5 native archived suffix; **recording**. | SEEDABLE; partial-member variants HARNESS |
| F13 | **Two environments / archived data / scoped subset.** Harness environments `fixture-a` and `fixture-b`, labels `Fixture A` and `Fixture B`, each has F01 identity in its own checkout. Include one stopped archived synthetic thread on each; same fixed timestamps. For override popover include four project overrides across both targets. Use a selected-one-environment variant for scoped members. | **Affected.** Environment badge consumes width, archive headings use the group label only when archived *threads*, not just checkouts, span >1 environment. One environment retains physical archive headings. Settings filters retain selected group identity; native direct callers recompute from the selected members. | C4 labelled web harness, **recording**. Native phone F08 covers the same one-member helper output; no second native environment is necessary for text generation. | HARNESS; real alternative is two separately paired isolated servers with synthetic state |
| F14 | **Azure clone-route segment and extra tails.** Two `my-repo` folders with origin `https://dev.azure.com/example-org/Platform/_git/_full/my-repo`; variant `https://example-org.visualstudio.com/Platform/_git/_optimized/my-repo`. Keep titles `my-repo`. Diagnostic extra-tail remote: `https://dev.azure.com/example-org/Platform/_git/my-repo/extra`, title `extra`. | **Affected, pinned-head defect.** Full-route input: `example-org/Platform/_git/_full/my-repo` → actual `Platform/_full`; expected readable repository label is `Platform/my-repo`. Optimized variant gives actual `Platform/_optimized`. Extra-tail diagnostic yields `Platform/my-repo` rather than falling back to the full existing path. No layout variant is needed: this is a distinct, incorrect label value. | Add one short F14 chapter to C1 after ordinary inputs, or a separate freshly seeded phase. The same sidebar/Settings row renders it; no full repeat across clients is necessary. Must not count the incorrect label as a successful after result. | SEEDABLE; EVALUATED; PINNED-HEAD DEFECT |

## Render-path matrix

The changed pure helper has exactly three callers: the multi-member branch in `buildProjectGroups`, mobile `useSettingsIndexSections`, and mobile `ProjectOverviewContent`. The registry hook is called only by the new grouping adapter; only Azure implements it. This inventory traces the resulting strings, not unrelated application state machines.

| Row | Surface and all label-bearing render paths | Layout/data/interaction branches to retain | App route or harness seed | Affected? / proof / status |
| --- | --- | --- | --- | --- |
| S01 | **Web/desktop sidebar project filter.** `Sidebar` maps group label to option label, selected-filter trigger tooltip/accessible name, pin button title, Settings button title, thread context-menu project-filter action, and `No threads in <label> yet`. | Unselected/all, one selected, multiple selected (joined tooltip / multi-icon trigger), query match/no-match, hover/focus, pin/unpin, archive exclusion. List text truncates; popup is capped at 18rem/available width. | `/`; compact suite; select F01, Shift-select F02, reset. Empty synthetic thread list supplies the label-bearing empty message. Context-menu item needs a synthetic stopped thread; see C3. | Yes; C1 **recording**; SEEDABLE |
| S02 | **Web/desktop sidebar content.** `SidebarThreadRow` card project line, thread hover-card project line, search-result cards, `SidebarDraftBlock` project line, and `SidebarProjectFolderHeader` text/new-thread tooltip/accessible name. | Ordinary cards vs folder layout; folder open/closed (count/dot can take width); pinned/active/settled/search rows share the same label prop. Single/two-line folder children do not compute a different label. Draft and persisted row are separate components. | Create a local draft with New thread without sending. For a persisted stopped row, use `orchestration.dispatchCommand` `thread.create` (no message dispatch); an alternative is a labelled renderer harness. Toggle `/settings/labs` → “Boring sidebar (group by project)” and “Two-line folder rows”. | Yes; C1/C3 **recording**; SEEDABLE / renderer HARNESS alternative |
| S03 | **Web/desktop command palette.** `buildProjectSearchItems` root project titles; `buildNewThreadInProjectItems` New thread in… results; contextual New thread in <label>; Pin/Unpin project and Project settings descriptions. Group member metadata remains physical paths/environment labels. | Root search, new-thread submenu, current/preferred vs other project, query/no-match, pinned/unpinned, remote-location metadata. Archived groups are omitted from pickers but can remain contextual Settings targets. Keyboard shortcuts route here too. | Open palette at `/`, search decoded `My Project`, enter New thread in…, then Project settings / Pin project contextual actions. No provider turn required. | Yes; C1 **recording**; SEEDABLE |
| S04 | **Web/desktop draft project picker.** `DraftHeroHeadline` options, per-option full-label tooltip, and pin title. | Option names clamp to two lines with anywhere wrapping; long/narrow case differs from short. Search/select/pin, preferred project and optional environment badge. **The displayed hero heading and its selected-title tooltip use physical `activeProject.title`, so they are not changed by this diff.** | Start a local New thread draft; open its project selector and search F06. C4 supplies the badge variant. | Options yes, heading no; C2 **recording**; SEEDABLE |
| S05 | **Web/desktop Settings lists/scope.** `SettingsScopeNotice` project-choice rows, checkout-choice section headings; `SettingsScopeSentence` project trigger/options; `useSettingsProjectGroups` sorts by the new label. | All vs selected vs missing project, <=6 radio options vs >6 searchable options, archived secondary text, duplicate display names requiring machine/path secondary text, single vs multiple environments, checkout-scoped selection. Device-only Settings paths suppress the scope sentence. | `/settings/projects`; selected URL is `/settings/projects?project=<encoded logical key>`, optional `machine=<id>&checkout=<encoded physical key>`. `/projects/<encoded key>` redirects here. Compact then extended suite; select through the UI to obtain exact keys. Checkout-choice section can be forced by `SettingsScopeNotice target="checkout"` in C4. | Yes; C1/C2/C4 **recording**; SEEDABLE, checkout-choice HARNESS if no real page requests it |
| S06 | **Web/desktop project detail and dialogs.** Project Name input default/key; image-file picker group heading when files exist; grouped removal-confirmation label. | Name focus/blur/Enter, unedited vs edited, uniform custom name and restore; permissions disable controls without changing label. Image picker: nonempty results show label, empty/loading/error results do not. Remove >1 members uses group displayName; a single member uses its physical title. The separate icon-symbol dialog uses representative.title and is unchanged. | `/settings/projects?project=<key>`. “Choose file” with synthetic `fixture-icon.svg`; search then close. Edit Name/blur, restore. “Remove all entries” then **Cancel**. No files or projects need to be removed. | Yes where noted; C1/C2/C3 **recording**; SEEDABLE |
| S07 | **Web/desktop other Settings text.** `settingsLayout.projectOverrides` → `SettingInheritance` clickable project labels. `ProjectDictionaryRow` interpolates `scope.label` into the Voice description for project/checkout scope. | Inheritance popover: no overrides vs 1–3 displayed; >3 folded only with multiple environments; expand and scroll. Voice prose wraps for long labels; no project scope uses unchanged shared-dictionary text. Reconnect errors inspected in Settings use environment-only labels and are **not** an extra affected error path. | Set a synthetic per-project setting at `/settings/threads`, return to environment/all scope and open its inheritance popover. `/settings/voice?project=<key>` shows project dictionary prose. C4 supplies >3/multi-environment fold state. | Yes; C2/C4 **recording**; SEEDABLE / folded variant HARNESS |
| S08 | **Web/desktop Archive.** `ArchivedThreadsPanel` group/section title is logicalGroup.displayName only when archived threads span >1 environment. | Loading/error/empty branch has no project label. One-environment loaded branch uses physical project.title, unchanged. Two-environment loaded branch uses derived label. Relative-time strings are time-dependent. | `/settings/archived` with two synthetic environment snapshots, each containing an archived stopped thread; C4 harness controls snapshots and clock. | Yes only multi-environment headings; C4 **recording**; HARNESS |
| S09 | **Mobile Home / thread sidebar filters.** `buildHomeProjectScopes` → `HomeRouteScreen` / `ThreadNavigationSidebar` → native header menu project items. `HomeScreen` → `ThreadListFilters` project-chip text/menu items. | All/selected/reset, no projects, archived excluded, pinned first; environment filtering can reduce a group to one member **before** building Home scopes. Phone vs split-view/sidebar changes host, not grouping algorithm. | Pair a pooled native client with isolated dev server; open Threads filter; choose F01, reset. Phone chip and native header hosts share the same items; a tablet/sidebar capture is optional because no new label/layout rule is introduced by that host. | Yes for >1-member scopes; C5/C6 **recording**; SEEDABLE |
| S10 | **Mobile New Task project selection.** `new-task-flow-provider` builds scopes; `NewTaskRouteScreen` shows scope.title, accessibility name, and passes it into ProjectFavicon. | Search decoded label, no-match, grouped “2 workspaces” subtitle vs singleton path. iOS uses a wrapping Pressable/Text row; Android uses NavigationRow with a two-line title. Automatic icon/unsupported-lucide fallback can change monogram and color because its title is the group label; fixed icon/favicon does not. | Open New Task on pooled iOS and Android; compact/extended seed; leave before sending. Use no image icon for automatic fallback, then explicit emoji control if inspecting glyph behavior. | Yes; C5/C6 **recording**; SEEDABLE |
| S11 | **Mobile Settings scope menu everywhere.** `useSettingsEnvironmentMenu` is consumed by root Settings, project overview, server controls (Threads/Source control/Worktrees/Maintenance), Providers, Scheduled tasks, and the workspace Settings toolbar. Selected submenu title and item labels use group.label; archived items add ` · Archived`. | All/selected/unavailable, environment selection, archived suffix, native iOS vs Android menus, phone/header vs workspace toolbar. A selected label is not recomputed from filtered members here. | Settings filter → Projects on pooled client; choose F01/F08/F12 and visit root / project / Threads. Other pages use the identical menu builder and need no repeat per page. | Yes for multi-member label; C5/C6 **recording**; SEEDABLE |
| S12 | **Mobile Settings index and overview.** Root “Projects” trailing value, overview two-line heading, Name input, and grouped removal confirmation. | **Direct helper call for any nonempty scoped member set, including one.** Root uses selectedGroup.label only when no scoped members. Overview has choose-project / unavailable / content branches. Name default/draft/saving/Save enabled, blank no-save, rename restore. Heading is max two lines; input is single-line. Remove multi vs single name follows S06. | `SettingsProjectOverview` after selecting group; `SettingsRouteScreen` index after returning. Use singleton suite for the one-checkout path and mobile General “Keep separate” variant. Rename only synthetic records; removal confirmation must be cancelled. | Yes, including singleton; C5 **recording**; SEEDABLE |
| S13 | **Mobile server Settings project override header.** `SettingsServerControlsRouteScreen` → `SettingsProjectOverridesSection.projectLabel` in Threads, Source control, Worktrees, Maintenance. | Label max two lines; “Use defaults” takes width only when hasOverrides and not pending. Read-only disables action; pending hides it but keeps label. Disconnected/no-target branch has no label. | Select F06 in Settings filter → Threads; change one project-scoped setting, show header with “Use defaults”; revert after capture. If a stable pending phase is needed, use labelled harness with `hasOverrides=true` and controlled `pending` transition. | Yes; C6 **recording**; SEEDABLE, pending HARNESS |
| S14 | **Mobile pending task/draft rows.** Home and ThreadNavigationSidebar pass grouped project title into `ThreadListV2PendingRow` → `ProjectFavicon`. | Only fallback icon input changes, **not the task title text**. Stable unsent local draft vs queued/transient row; screen vs sidebar share this prop. Normal persisted mobile thread cards still use physical project.title. | Start an unsent local draft in F01 and leave it in Threads. Deterministic queued/pending phase: labelled native renderer harness with the same group title and synthetic task, controlled waiting flag; do not start a paid provider turn just to hold the row. | Fallback glyph can change; C5/C6 **recording**; SEEDABLE draft / HARNESS transient |
| S15 | **Explicit unchanged/non-render consumers.** Onboarding imported-repository labels, Add Project to Machine repositoryIdentity.displayName, mobile keyboard palette and existing thread-card project titles, PR project filters, shortcut group counting, physical project records. | Their strings come from physical project.title or raw repository identity. Mobile PR filters use grouped scopes only to order project references, not to display this group label. Scratch grouped “No project”, empty project sets, physical duplicates, no identity, loading/error placeholders, and capability/permission errors introduce no new label layout. | Existing tests / synthetic evaluation; no capture needed. Provider-adapter, wire-contract, relay/tunnel and agent-tool paths are unchanged: label derivation runs locally in each client from the same snapshot. | No; CODE-ONLY / status COVERED |

## Minimum capture set

Capture each required row **before and after** against the pinned base/head, with the same synthetic fixture data and viewport. Each session can contain multiple labelled chapters rather than one file per caller. This is six short capture sessions, not a forge × remote × theme × client Cartesian product. Only C4 and specifically named transient states may be represented as harness evidence. Retain raw clips; still frames may be extracted for easy comparison but do not replace recordings.

| Capture | Viewport × theme × platform | Required rows / fixture phase | Format and actions | Status |
| --- | --- | --- | --- | --- |
| C1 | 1280×900 CSS px × dark × web | S01–S03, S05–S06; F01/F02/F05/F07 compact suite, F03/F12 as sequential changes, F14 clone-route phase | **recording**: open filter, show HTTPS/SSH/fork/GitHub rows, search, select then wait for trigger tooltip, multi-select/reset; inspect empty text; create unsent draft; palette root/submenu/context actions; Settings list, Name rename/restore, pin/archive and reverse. With a synthetic stopped thread also show card/hover/search/folder-open/folder-closed. | PLANNED; SEEDABLE |
| C2 | 390×844 CSS px × light × web | S04–S07; F06/F11/F12 extended suite | **recording**: reveal responsive sidebar, long-label picker and tooltip, Settings >6 searchable selector including duplicate labels and archived secondary text, select each distinct key, long Name field, Choose file with nonempty results, project Voice description. If <=6 selector was not shown in C1, show it in a compact phase first. | PLANNED; SEEDABLE |
| C3 | 1280×900 content px × dark × Electron desktop | S01/S02/S06; F01 + one stopped synthetic thread | **recording**: same shared web text inherits C1 proof; independently show native thread context-menu project-filter item and grouped removal confirmation, then Cancel. Electron native menu/dialog text cannot be proven by a browser still. A stopped thread can be injected with the typed `thread.create` RPC without dispatching a message. | PLANNED; SEEDABLE |
| C4 | 1280×900 CSS px × dark × labelled web harness | S05/S07/S08; F13 + F11 collision; F01 labels | **recording** with visible “HARNESS — synthetic environments” label: two-environment badge/scoped choices; 1 vs 4 override rows and expand/collapse; archived snapshots loading → ready across two environments; fixed synthetic clock. Also compare one-environment archive title and no-match/empty/loading/error label absence without pretending they are changed. | PLANNED; HARNESS SPECIFIED |
| C5 | approximately 390×844 logical px × dark × pooled iOS phone | S09–S12/S14; grouped compact phase then F08 singleton phase; F12 archived variant | **recording**: Home chip/native filter, New Task list and label search, automatic icon fallback, Settings menu → Projects index value → overview heading/Name, one-checkout exception, edit/restore, removal cancel. Stable unsent draft covers pending-row icon; a transient-only variant must be labelled harness. | PLANNED; SEEDABLE; pooled device not leased in pass 1 |
| C6 | approximately 412×915 logical px × light × pooled Android phone | S09–S11/S13/S14; extended suite, F06 long row | **recording**: Android native menus and its distinct two-line NavigationRow, long Settings override header with/without Use defaults, automatic/fixed icon control, unsent draft fallback. Shared parser output does not substitute for Android wrapping. | PLANNED; SEEDABLE; pooled device not leased in pass 1 |

The native device’s actual logical size should be recorded if the pool offers a different phone. Do not claim a desktop browser’s mobile viewport as native proof. Use the device-pool/test-t3-mobile workflow when capture is authorized. No additional macOS/Windows/Linux web-content permutation, high-contrast/custom-theme permutation, streamed provider-status permutation, or remote transport permutation is justified by this text-only diff. Theme does not affect the string or introduce a new branch; light and dark are sampled across the sessions. No new hover animation, loading lifecycle, or timer was introduced.

## Seeding and harness procedure

### Real app

1. Capture lead starts a **fresh isolated dev server** in the approved checkout with `T3CODE_DEV_SEED=0 vp run dev`. Do not use the live T3 home, copied user data, baked Vite origins, or another lead’s checkout. Read actual ports from dev-runner output. Keep the spawned PID and stop only that process when finished. This audit did not start a server.
2. For each fixture, create `.../<new timestamp>/repos/<fixture>/1/<repo-name>` and `.../2/<repo-name>` **under this audit scratch directory**, run `git init`, add the specified origin (and upstream for F05), make an empty commit using synthetic local author settings, and add it to the app. The repo folder basename supplies the default project title. Do not fetch, clone, authenticate to a forge, or require these remotes to exist.
3. From `<checkout>/apps/server`, invoke `node <checkout>/apps/server/src/bin.ts project add --base-dir <checkout>/.t3 <absolute fixture repo path>`; use `--title "Team Console"` only for a custom-title fixture. This absolute script spelling avoids accidentally resolving `apps/server/apps/server/src/bin.ts`. Wait for repositoryIdentity to be present in the app snapshot, not for an arbitrary timer. Refresh/discovery is asynchronous, but the label calculation itself is synchronous.
4. A prepared opt-in driver is at `/tmp/issue-lead-33607-r1/proof-audit-1/seed-real-fixtures.py`. Its `compact` suite makes F01/F02/F05/F07 (four logical groups); `extended` adds F06, F08, a separate custom group, and the second-organization F11 collision. Other suites are `mixed`, `fallback`, `legacy`, `singleton`, `clone-route`. The clone-route suite supplies F14 in its own phase. It uses argument arrays, the absolute CLI path, synthetic authors, a local `fixture-icon.svg`, and a new timestamped run directory containing manifest/log on every invocation. **It was syntax-checked, not executed against the app.** Run e.g. `python3 /tmp/issue-lead-33607-r1/proof-audit-1/seed-real-fixtures.py --checkout <checkout> --suite compact` only once the isolated server is ready.
5. Run mutually exclusive variants in separate fresh phases, or rename the existing members through `project rename <path> <title> --base-dir <checkout>/.t3`. Do not seed multiple competing F01/F03/F04 title variants into one group and then expect a two-member fixture. Fresh phases use new run directories; never delete prior evidence. Compact vs extended phases need the actual Settings option count verified, because an app-created scratch project may add a group.
6. Web global grouping is `/settings/general` → Project grouping. Per-checkout overrides and repository-path/separate modes are in project Settings’ checkout grouping selector. Mobile keeps its own General grouping preference and ignores web per-checkout overrides. Switch the mobile preference explicitly for F08’s separate-mode check.
7. Name, pin, archive, and setting-override phases use app controls or the existing typed project/settings RPCs on this isolated environment. Use native Settings General “Group by repository” first. To inspect removal, cancel the confirmation. Use “Choose file” for the image-file picker label; the symbol/icon picker is an unchanged physical-title path.
8. For a stopped thread record, the authenticated app RPC is `orchestration.dispatchCommand` with a typed `thread.create` command: fresh command/thread IDs, the fixture project ID, synthetic title, a model selection from the dev server’s config, runtime/interaction modes and branch/worktree fields as required by the schema, plus createdBy/creationSource. **Do not dispatch a message.** Alternatively render the existing row in a labelled harness. An unsent New thread draft needs no RPC/provider run. No real provider session is necessary for this label proof.

### Labelled harnesses

- **Identity harness (F09/F10):** feed the concrete identities above to `deriveProjectGroupLabel` / `buildProjectGroups`; use synthetic `EnvironmentProject` records with fixed timestamps and distinct IDs/paths. F09 omits only `origin.remoteUrl`; its second variant also omits `origin.displayName`. F10 removes display/name fields and varies titles/names to reach the stated fallbacks. Schema-invalid empty titles are explicitly helper-only, not alleged production states.
- **Multi-environment renderer harness (C4/F13):** provide two environment presentations and two sets of F01 project records, connected with project-organization capability; include matching Settings scope/group contexts. Archived snapshots must each contain an archived stopped thread, because `hasMultipleEnvironments` is derived from visible archived threads. With only one snapshot containing threads, assert the old physical-title heading instead. Freeze synthetic relative timestamps, and visibly label the harness. A real alternative requires pairing two separately isolated servers; one server cannot mint a second environment by project-add.
- **Inheritance harness:** use `SettingInheritance` with two matching synthetic targets/server settings, a real project-scoped key such as `defaultThreadEnvMode`, `state="inherited"`, and 1 then 4 `overridingProjects` entries (`environmentId`, `projectId`, label from F01/F06, inert `open` callbacks); put effective override values in the synthetic server config’s projectSettingsOverrides. Matching target chains with multiple environments expose the >3 folding branch. Keep the component’s real popover and Collapsible implementation; record opening and expanding it.
- **Native transient harness:** render `ThreadListV2PendingRow` with a synthetic pending task and F01 group title, no explicit icon/favicon, then a fixed explicit emoji. Control a waiting → ready transition if demonstrating a transient phase; record it. This is a fallback-icon check, not a claim that native persisted thread cards now print grouped names. For S13 use `SettingsProjectOverridesSection` with F06 label, `hasOverrides=true`, `disabled=false`, and controlled pending true/false; record the button appearing. Current real unsent drafts and saved override headers can be captured without these harnesses.

## Evidence basis and boundaries

The first scratch-only Node driver imported the checkout modules and extracted the base helper with TypeScript stripping. After concurrent source edits appeared, a second driver loaded grouping and Azure definition source from the pinned git commit, rewrote only import locations into scratch, and used the unchanged built-in registry recognition to select the pinned Azure hook. Its definitive run evaluated 26 synthetic grouping/direct-call shapes plus registry parser variants. It confirmed ordinary HTTPS/SSH output, mixed transports/case, custom/divergent/previous/new-label titles, origin precedence, missing origin remote, legacy URLs, unknown/unreadable remotes, singleton vs direct calls, long/escaped/invalid-escape strings, representative fallback, empty titles, scratch, separate, and repository-path roots. It did not run a browser, render test, repo-wide test, or native client, and is not claimed as capture evidence. Definitive raw synthetic output is retained at `/tmp/issue-lead-33607-r1/proof-audit-1/20261110T000200Z-pinned/fixture-results.json` and `fixture-results.log`; the driver and pinned source copies are adjacent. The earlier run is also retained under `20261109T234900Z`; nothing was overwritten or deleted.

Source anchors, all at the audited head:

- [Grouping adapter, precedence and group-size gate](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/packages/client-runtime/src/state/projectGrouping.ts#L202), [Azure parser](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/packages/source-control-azure-devops/src/shared/definition.ts#L59), [registry recognition/hook](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/packages/source-control-core/src/shared/definition.ts#L690), [identity origin fallback contract](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/packages/contracts/src/environment.ts#L202), [server remote selection and discovery](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/server/src/project/RepositoryIdentityResolver.ts#L77).
- [Web snapshot label mapping](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/web/src/sidebarProjectGrouping.ts#L194), [sidebar](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/web/src/components/Sidebar.tsx#L3046), [folder header](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/web/src/components/sidebar/SidebarProjectFolderHeader.tsx#L80), [thread hover/card](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/web/src/components/sidebar/SidebarThreadRow.tsx#L280), [draft row](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/web/src/components/sidebar/SidebarDraftBlock.tsx#L107), [palette project titles](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/web/src/components/CommandPalette.logic.ts#L388), [palette contextual actions](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/web/src/components/CommandPalette.tsx#L1266), [draft options/tooltips](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/web/src/components/chat/DraftHeroHeadline.tsx#L341).
- [Settings project choices](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/web/src/components/settings/SettingsScopeNotice.tsx#L85), [scope menu/count boundary](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/web/src/components/settings/SettingsScopeSentence.tsx#L155), [detail name](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/web/src/components/settings/ProjectSettingsPanel.tsx#L542), [file picker heading](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/web/src/components/settings/ProjectFaviconPickerDialog.tsx#L132), [removal label](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/packages/client-runtime/src/state/projectRemoval.ts#L32), [override mapping](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/web/src/components/settings/settingsLayout.tsx#L294), [inheritance fold](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/web/src/components/settings/SettingInheritance.tsx#L386), [Voice description](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/web/src/components/settings/VoiceSettingsPanel.tsx#L435), [Archive conditional](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/web/src/components/settings/ArchivedThreadsPanel.tsx#L108).
- [Mobile group-to-title mapping](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/mobile/src/features/home/homeThreadList.ts#L53), [Home filter chip](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/mobile/src/features/home/ThreadListFilters.tsx#L63), [thread sidebar menu](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx#L177), [New Task platform branches](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/mobile/src/features/threads/NewTaskRouteScreen.tsx#L475), [Settings singleton index](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/mobile/src/features/settings/SettingsRouteScreen.tsx#L224), [Settings singleton overview](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/mobile/src/features/settings/SettingsProjectOverviewRouteScreen.tsx#L141), [Settings scope menu](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/mobile/src/features/settings/components/SettingsEnvironmentFilterHeader.tsx#L46), [override section](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/mobile/src/features/settings/components/SettingsProjectOverridesSection.tsx#L18), [pending-row icon input](https://github.com/pingdotgg/t3code/blob/245a3b48f54247d58d2259cc58211b7e660ff946/apps/mobile/src/features/threads/thread-list-v2-items.tsx#L460).

Coverage: all modified production functions and their label consumers were traced in web, desktop’s shared web UI/native menu boundary, and mobile. Remaining capture work belongs to pass 2. F14 is already a reproduced implementation defect at the pinned head; a future head that addresses it needs its own check. This pass-1 verdict means the matrix is complete and seedable, not that the fix has passed visual or correctness proof. No server was started, so none requires stopping. Free disk space remained 11 GB; no evidence was deleted.

FRICTION: other | Four source/test files acquired concurrent uncommitted edits while the requested head stayed fixed; the definitive evaluation had to be rerun from pinned source copies in scratch. Those concurrent edits later became staged; they remain outside this audit. | Start proof audits from an immutable source snapshot, or announce concurrent edits and the replacement head explicitly.

VERDICT: SUFFICIENT
