# fix\(web\): explain why older connections cannot update servers · PR \#23374 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/23374)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: open

Source repository: pingdotgg/t3code

Source branch: fix/issue-17034

Target branch: main

Head commit: a1a73e8205729d745d78a6c318041c93e6531ecf

[Discussion and reviews](https://git.cafe/pingdotgg/t3code/pull/23374)

[Commits](https://git.cafe/pingdotgg/t3code/pull/23374/commits?format=markdown)

[Files changed](https://git.cafe/pingdotgg/t3code/pull/23374/diffs?format=markdown)

## Description

---

## Problem

Connections paired before the maintenance-permission split can lack permission to update their server. Their Update buttons give no explanation, and Update all remains enabled even when it will silently skip every target.

Reproduced in the real web dev server on Linux with a legacy-scoped connection and an update-available server descriptor fixture.

## Change

Explain denied updates with pairing guidance available through hover, keyboard focus, screen-reader descriptions, and a tap warning. Disable Update all when no target permits updates, and name denied servers skipped by a mixed update. Reuse the existing per-environment session subscription and retain the existing execution-time permission checks.

Include a reproducible isolated fixture script for identity, session, project, and update-descriptor setup.

## Scope and approval

Fixes #17034
GitCafe: https://git.cafe/pingdotgg/t3code/issues/17034
Triaged bug via the automated issue flow. The fix covers the shared web update controls used by Connections and composer banners.

## Verification

CI follow-up at [a1a73e8205](https://github.com/pingdotgg/t3code/commit/a1a73e8205729d745d78a6c318041c93e6531ecf): replaced unsupported `Promise.withResolvers`, registered the evidence CLI with Knip, and excluded loading/failed sessions from the bulk update count with two regression cases. `git diff --check` passed. Scoped format/lint/Knip/typecheck attempts timed out or stalled without diagnostics; the updated 12-case browser suite remains unrun locally. GitCafe now reports all 29 checks successful on this head, including Lint, Build and Typecheck, Test Web, Test, and Check. The three bot findings are resolved. Earlier captures below predate this follow-up.

- Three focused regressions failed on the original implementation: unexplained single-server denial, enabled all-denied bulk action, and silent skipping of a named target.
- The focused browser suite passed all nine tests after implementation and subscription simplification. A fourth regression was then added for tapping the mixed-target wrapper while an update is pending; the hint now distinguishes pending state from permission denial. Scoped lint and formatting passed for that correction.
- The first post-merge browser rerun could not connect Chromium within 420 seconds and ran no tests ([full output](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/91d18ab486e0cd49/17034-final-browser.log)). The web TypeScript check passed before the subscription simplification. The final TypeScript rerun stalled for over 40 minutes under VM load and was stopped; final-head typechecking remains unverified.
- A second, serialized browser attempt against all ten tests reached its 600-second limit before Vitest started. The new pending-state regression remains locally unrun.
- Integrated web pass: a legacy-scoped saved connection produced a disabled Update all control; tapping its wrapper showed pairing guidance naming the server. The saved-server icon exposed the same explanation.

Evidence is partial. The screenshots below were captured at different session states and font settings; they are endpoint evidence, not a matched pixel comparison. The [full fixture matrix](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/65e8d82da403340e/issue-17034-proof-audit-pass1.md) remains unverified across 1440/390 widths and light/dark themes. The capture audit found 0/19 affected rows complete. VM overload caused browser connection failures, authentication timeouts, and server event-loop stalls up to 107 seconds.

| Before: enabled bulk action with no feedback | After: disabled bulk action and pairing explanation |
| --- | --- |
| ![Before: Connections offers Update all for a denied legacy connection](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/a67504177fc3d3b4/browser-screenshot-localhost-mviwsx85-8e2ff6f0.png) | ![After: Connections explains that fs-34 must be paired again to permit updates](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/9f19995200e6929a/browser-screenshot-localhost-mviywq0a-ef9139ea.png) |
| Original main a50cd989ae, 1280×800/light | Implementation 4eb9032d54, 1280×800/light |

[Before recording: clicking Update all produces no update or notice](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/6218fdb428e163f3/mcp-launch-client-3a4d8f55b3-b4a3-4452-a0bb-549d6f59fde1-fwd-3a4d5f79b403da441c0-162a784f-b0ff-4695-a9b2-2e72181aba13-mp4.mp4)

Rendering environment: T3 Browser panel, Chromium 154 on Linux, no device emulation, DPR 2, CSS viewport 1280×800, light theme. Font settings and session privileges differ between these captures. Visible differences include font family, primary server settings and authorized-client rows, vertical row positions, disabled bulk action, and the new warning toast. No claim of unchanged pixels is made.

Unverified: matched final-head recordings, pixel diffs, narrow/dark layouts, long labels, retry/stacked composer banners, auto-balance variants, actual re-pair transitions, transient session and permission races, touch emulation, and Electron confirmation/filtering. Native mobile was not changed or verified. The descriptor fixture models update availability; it does not run a historical server binary. Fixture reconstruction remains unverified.

Models: Claude Opus 5.5 and GPT-6.1-Sol. Harnesses: Claude Code and Codex through T3 Code.

