fix(server): keep older clients connected after keybinding additions - #5018

Closed
PixPMusic wants to merge 2 commits into
pingdotgg:mainfrom
PixPMusic:fix/keybinding-wire-compat
Closed

fix(server): keep older clients connected after keybinding additions#5018
PixPMusic wants to merge 2 commits into
pingdotgg:mainfrom
PixPMusic:fix/keybinding-wire-compat

Conversation

@PixPMusic

@PixPMusicPixPMusic commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Negotiate the supported keybinding command set once per WebSocket connection.
  • Keep filePicker.toggle and projectSearch.toggle out of config and mutation responses for clients that predate the negotiation.
  • Preserve the full keybinding list for current web, desktop, and mobile clients.
  • Cover initial config, streamed config updates, and keybinding upsert/remove responses.

Why

Nightly 0.0.32-nightly.20260730.957 began returning two new default keybinding commands. The 0.0.31 client validates those commands against a closed schema while establishing its connection, so it rejects the initial server config and never reaches the connected state.

Current clients now advertise support through the WebSocket URL. Older clients omit the capability and receive a legacy-safe wire projection without changing the persisted or in-memory keybinding configuration. Older servers ignore the additional query parameter.

Verification

  • vp test run packages/client-runtime/src/rpc/session.test.ts apps/server/src/server.test.ts — 121 tests passed
  • vp run --filter @t3tools/contracts --filter @t3tools/client-runtime --filter t3 typecheck
  • targeted lint and formatting checks for all changed files
  • git diff --check origin/main...HEAD

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I added regression coverage

Model: GPT-5.6 Sol | Harness: Codex in T3 Code


Note

Medium Risk
Wire compatibility logic on the hot WebSocket path; mistakes could hide keybindings from current clients or still break legacy connects, but scope is limited to response projection with strong test coverage.

Overview
Adds WebSocket keybinding command-set negotiation so nightly servers can expose filePicker.toggle and projectSearch.toggle without breaking 0.0.31 clients that validate keybindings against a smaller schema during connect.

Contracts define WS_KEYBINDING_COMMAND_SET_QUERY_PARAM and CURRENT_KEYBINDING_COMMAND_SET_VERSION. Client runtime appends that query param when opening the WebSocket. Server reads it on /ws upgrade and, for connections without a current version, filters those two commands from keybinding payloads on serverGetConfig, upsert/remove responses, and subscribeServerConfig streams—without changing persisted config.

Regression tests cover unnegotiated, legacy (0), current, and future client versions across config, streams, and mutations.

Reviewed by Cursor Bugbot for commit 8785f3d. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Filter keybinding commands for older clients after new keybinding additions

  • Adds WS_KEYBINDING_COMMAND_SET_QUERY_PARAM and CURRENT_KEYBINDING_COMMAND_SET_VERSION (1) to rpc.ts as a versioning mechanism for keybinding command sets.
  • Clients advertise support by appending keybindingCommandSet=<version> to the WebSocket URL in session.ts.
  • The server in ws.ts inspects this query param and, for clients not advertising the current version, strips filePicker.toggle and projectSearch.toggle from keybindings in serverGetConfig, serverUpsertKeybinding, serverRemoveKeybinding, and subscribeServerConfig responses.
  • Behavioral Change: older or unversioned clients now receive a filtered keybinding set that omits the two new commands.

Macroscope summarized 8785f3d.

@coderabbitai

coderabbitaiBot commented Jul 30, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 54035889-0c45-499e-b0a8-f7d09bcb58c8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Jul 30, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Jul 30, 2026
@macroscopeapp

macroscopeappBot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This backwards-compatibility fix modifies what keybindings are sent to websocket clients based on version negotiation. While the change is well-scoped and thoroughly tested, it touches core server-client communication in ws.ts and the author is not a frequent contributor to this file, warranting human verification.

You can customize Macroscope's approvability policy. Learn more.

@macroscopeapp
macroscopeappBot dismissed their stale reviewJuly 30, 2026 18:29

Dismissing prior approval to re-evaluate 8785f3d

@NikAtNight

Copy link
Copy Markdown

Confirmed this exact regression against production T3 Connect. With Nightly .958, both the iOS app (T3Code/18) and app.t3.codes successfully authenticated and upgraded the WebSocket, then disconnected immediately after server.getConfig. Rolling the desktop environment back to .953 restored connectivity for both clients. This matches the closed-schema/keybinding compatibility failure described here.

@juliusmarminge

Copy link
Copy Markdown
Member

this feels like the wrong solution to the problem

@PixPMusic

Copy link
Copy Markdown
ContributorAuthor

@juliusmarminge main concern was keeping the nightly server compatible with the released mobile client, as App Store review time can take a while.

If you don't care about nightlies being compatible--or want to start doing CI nightlies for Android at least?--then we can move in another direction.

@juliusmarminge

Copy link
Copy Markdown
Member

I do care about nightlies being compatible, but we've already submitted a new version for review and I'm not sure if we can do a new OTA right now ( I think build versions are in a rough state...) so we should just fix it properly

@juliusmarminge

Copy link
Copy Markdown
Member

#5055

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@PixPMusic@NikAtNight@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(server): keep older clients connected after keybinding additions - #5018

Closed
PixPMusic wants to merge 2 commits into
pingdotgg:mainfrom
PixPMusic:fix/keybinding-wire-compat
Closed

fix(server): keep older clients connected after keybinding additions#5018
PixPMusic wants to merge 2 commits into
pingdotgg:mainfrom
PixPMusic:fix/keybinding-wire-compat

Conversation

@PixPMusic

@PixPMusicPixPMusic commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Negotiate the supported keybinding command set once per WebSocket connection.
  • Keep filePicker.toggle and projectSearch.toggle out of config and mutation responses for clients that predate the negotiation.
  • Preserve the full keybinding list for current web, desktop, and mobile clients.
  • Cover initial config, streamed config updates, and keybinding upsert/remove responses.

Why

Nightly 0.0.32-nightly.20260730.957 began returning two new default keybinding commands. The 0.0.31 client validates those commands against a closed schema while establishing its connection, so it rejects the initial server config and never reaches the connected state.

Current clients now advertise support through the WebSocket URL. Older clients omit the capability and receive a legacy-safe wire projection without changing the persisted or in-memory keybinding configuration. Older servers ignore the additional query parameter.

Verification

  • vp test run packages/client-runtime/src/rpc/session.test.ts apps/server/src/server.test.ts — 121 tests passed
  • vp run --filter @t3tools/contracts --filter @t3tools/client-runtime --filter t3 typecheck
  • targeted lint and formatting checks for all changed files
  • git diff --check origin/main...HEAD

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I added regression coverage

Model: GPT-5.6 Sol | Harness: Codex in T3 Code


Note

Medium Risk
Wire compatibility logic on the hot WebSocket path; mistakes could hide keybindings from current clients or still break legacy connects, but scope is limited to response projection with strong test coverage.

Overview
Adds WebSocket keybinding command-set negotiation so nightly servers can expose filePicker.toggle and projectSearch.toggle without breaking 0.0.31 clients that validate keybindings against a smaller schema during connect.

Contracts define WS_KEYBINDING_COMMAND_SET_QUERY_PARAM and CURRENT_KEYBINDING_COMMAND_SET_VERSION. Client runtime appends that query param when opening the WebSocket. Server reads it on /ws upgrade and, for connections without a current version, filters those two commands from keybinding payloads on serverGetConfig, upsert/remove responses, and subscribeServerConfig streams—without changing persisted config.

Regression tests cover unnegotiated, legacy (0), current, and future client versions across config, streams, and mutations.

Reviewed by Cursor Bugbot for commit 8785f3d. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Filter keybinding commands for older clients after new keybinding additions

  • Adds WS_KEYBINDING_COMMAND_SET_QUERY_PARAM and CURRENT_KEYBINDING_COMMAND_SET_VERSION (1) to rpc.ts as a versioning mechanism for keybinding command sets.
  • Clients advertise support by appending keybindingCommandSet=<version> to the WebSocket URL in session.ts.
  • The server in ws.ts inspects this query param and, for clients not advertising the current version, strips filePicker.toggle and projectSearch.toggle from keybindings in serverGetConfig, serverUpsertKeybinding, serverRemoveKeybinding, and subscribeServerConfig responses.
  • Behavioral Change: older or unversioned clients now receive a filtered keybinding set that omits the two new commands.

Macroscope summarized 8785f3d.

@coderabbitai

coderabbitaiBot commented Jul 30, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 54035889-0c45-499e-b0a8-f7d09bcb58c8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Jul 30, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Jul 30, 2026
@macroscopeapp

macroscopeappBot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This backwards-compatibility fix modifies what keybindings are sent to websocket clients based on version negotiation. While the change is well-scoped and thoroughly tested, it touches core server-client communication in ws.ts and the author is not a frequent contributor to this file, warranting human verification.

You can customize Macroscope's approvability policy. Learn more.

@macroscopeapp
macroscopeappBot dismissed their stale reviewJuly 30, 2026 18:29

Dismissing prior approval to re-evaluate 8785f3d

@NikAtNight

Copy link
Copy Markdown

Confirmed this exact regression against production T3 Connect. With Nightly .958, both the iOS app (T3Code/18) and app.t3.codes successfully authenticated and upgraded the WebSocket, then disconnected immediately after server.getConfig. Rolling the desktop environment back to .953 restored connectivity for both clients. This matches the closed-schema/keybinding compatibility failure described here.

@juliusmarminge

Copy link
Copy Markdown
Member

this feels like the wrong solution to the problem

@PixPMusic

Copy link
Copy Markdown
ContributorAuthor

@juliusmarminge main concern was keeping the nightly server compatible with the released mobile client, as App Store review time can take a while.

If you don't care about nightlies being compatible--or want to start doing CI nightlies for Android at least?--then we can move in another direction.

@juliusmarminge

Copy link
Copy Markdown
Member

I do care about nightlies being compatible, but we've already submitted a new version for review and I'm not sure if we can do a new OTA right now ( I think build versions are in a rough state...) so we should just fix it properly

@juliusmarminge

Copy link
Copy Markdown
Member

#5055

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@PixPMusic@NikAtNight@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(server): keep older clients connected after keybinding additions - #5018

Closed
PixPMusic wants to merge 2 commits into
pingdotgg:mainfrom
PixPMusic:fix/keybinding-wire-compat
Closed

fix(server): keep older clients connected after keybinding additions#5018
PixPMusic wants to merge 2 commits into
pingdotgg:mainfrom
PixPMusic:fix/keybinding-wire-compat

Conversation

@PixPMusic

@PixPMusicPixPMusic commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Negotiate the supported keybinding command set once per WebSocket connection.
  • Keep filePicker.toggle and projectSearch.toggle out of config and mutation responses for clients that predate the negotiation.
  • Preserve the full keybinding list for current web, desktop, and mobile clients.
  • Cover initial config, streamed config updates, and keybinding upsert/remove responses.

Why

Nightly 0.0.32-nightly.20260730.957 began returning two new default keybinding commands. The 0.0.31 client validates those commands against a closed schema while establishing its connection, so it rejects the initial server config and never reaches the connected state.

Current clients now advertise support through the WebSocket URL. Older clients omit the capability and receive a legacy-safe wire projection without changing the persisted or in-memory keybinding configuration. Older servers ignore the additional query parameter.

Verification

  • vp test run packages/client-runtime/src/rpc/session.test.ts apps/server/src/server.test.ts — 121 tests passed
  • vp run --filter @t3tools/contracts --filter @t3tools/client-runtime --filter t3 typecheck
  • targeted lint and formatting checks for all changed files
  • git diff --check origin/main...HEAD

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I added regression coverage

Model: GPT-5.6 Sol | Harness: Codex in T3 Code


Note

Medium Risk
Wire compatibility logic on the hot WebSocket path; mistakes could hide keybindings from current clients or still break legacy connects, but scope is limited to response projection with strong test coverage.

Overview
Adds WebSocket keybinding command-set negotiation so nightly servers can expose filePicker.toggle and projectSearch.toggle without breaking 0.0.31 clients that validate keybindings against a smaller schema during connect.

Contracts define WS_KEYBINDING_COMMAND_SET_QUERY_PARAM and CURRENT_KEYBINDING_COMMAND_SET_VERSION. Client runtime appends that query param when opening the WebSocket. Server reads it on /ws upgrade and, for connections without a current version, filters those two commands from keybinding payloads on serverGetConfig, upsert/remove responses, and subscribeServerConfig streams—without changing persisted config.

Regression tests cover unnegotiated, legacy (0), current, and future client versions across config, streams, and mutations.

Reviewed by Cursor Bugbot for commit 8785f3d. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Filter keybinding commands for older clients after new keybinding additions

  • Adds WS_KEYBINDING_COMMAND_SET_QUERY_PARAM and CURRENT_KEYBINDING_COMMAND_SET_VERSION (1) to rpc.ts as a versioning mechanism for keybinding command sets.
  • Clients advertise support by appending keybindingCommandSet=<version> to the WebSocket URL in session.ts.
  • The server in ws.ts inspects this query param and, for clients not advertising the current version, strips filePicker.toggle and projectSearch.toggle from keybindings in serverGetConfig, serverUpsertKeybinding, serverRemoveKeybinding, and subscribeServerConfig responses.
  • Behavioral Change: older or unversioned clients now receive a filtered keybinding set that omits the two new commands.

Macroscope summarized 8785f3d.

@coderabbitai

coderabbitaiBot commented Jul 30, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 54035889-0c45-499e-b0a8-f7d09bcb58c8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Jul 30, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Jul 30, 2026
@macroscopeapp

macroscopeappBot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This backwards-compatibility fix modifies what keybindings are sent to websocket clients based on version negotiation. While the change is well-scoped and thoroughly tested, it touches core server-client communication in ws.ts and the author is not a frequent contributor to this file, warranting human verification.

You can customize Macroscope's approvability policy. Learn more.

@macroscopeapp
macroscopeappBot dismissed their stale reviewJuly 30, 2026 18:29

Dismissing prior approval to re-evaluate 8785f3d

@NikAtNight

Copy link
Copy Markdown

Confirmed this exact regression against production T3 Connect. With Nightly .958, both the iOS app (T3Code/18) and app.t3.codes successfully authenticated and upgraded the WebSocket, then disconnected immediately after server.getConfig. Rolling the desktop environment back to .953 restored connectivity for both clients. This matches the closed-schema/keybinding compatibility failure described here.

@juliusmarminge

Copy link
Copy Markdown
Member

this feels like the wrong solution to the problem

@PixPMusic

Copy link
Copy Markdown
ContributorAuthor

@juliusmarminge main concern was keeping the nightly server compatible with the released mobile client, as App Store review time can take a while.

If you don't care about nightlies being compatible--or want to start doing CI nightlies for Android at least?--then we can move in another direction.

@juliusmarminge

Copy link
Copy Markdown
Member

I do care about nightlies being compatible, but we've already submitted a new version for review and I'm not sure if we can do a new OTA right now ( I think build versions are in a rough state...) so we should just fix it properly

@juliusmarminge

Copy link
Copy Markdown
Member

#5055

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@PixPMusic@NikAtNight@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(server): keep older clients connected after keybinding additions - #5018

Closed
PixPMusic wants to merge 2 commits into
pingdotgg:mainfrom
PixPMusic:fix/keybinding-wire-compat
Closed

fix(server): keep older clients connected after keybinding additions#5018
PixPMusic wants to merge 2 commits into
pingdotgg:mainfrom
PixPMusic:fix/keybinding-wire-compat

Conversation

@PixPMusic

@PixPMusicPixPMusic commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Negotiate the supported keybinding command set once per WebSocket connection.
  • Keep filePicker.toggle and projectSearch.toggle out of config and mutation responses for clients that predate the negotiation.
  • Preserve the full keybinding list for current web, desktop, and mobile clients.
  • Cover initial config, streamed config updates, and keybinding upsert/remove responses.

Why

Nightly 0.0.32-nightly.20260730.957 began returning two new default keybinding commands. The 0.0.31 client validates those commands against a closed schema while establishing its connection, so it rejects the initial server config and never reaches the connected state.

Current clients now advertise support through the WebSocket URL. Older clients omit the capability and receive a legacy-safe wire projection without changing the persisted or in-memory keybinding configuration. Older servers ignore the additional query parameter.

Verification

  • vp test run packages/client-runtime/src/rpc/session.test.ts apps/server/src/server.test.ts — 121 tests passed
  • vp run --filter @t3tools/contracts --filter @t3tools/client-runtime --filter t3 typecheck
  • targeted lint and formatting checks for all changed files
  • git diff --check origin/main...HEAD

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I added regression coverage

Model: GPT-5.6 Sol | Harness: Codex in T3 Code


Note

Medium Risk
Wire compatibility logic on the hot WebSocket path; mistakes could hide keybindings from current clients or still break legacy connects, but scope is limited to response projection with strong test coverage.

Overview
Adds WebSocket keybinding command-set negotiation so nightly servers can expose filePicker.toggle and projectSearch.toggle without breaking 0.0.31 clients that validate keybindings against a smaller schema during connect.

Contracts define WS_KEYBINDING_COMMAND_SET_QUERY_PARAM and CURRENT_KEYBINDING_COMMAND_SET_VERSION. Client runtime appends that query param when opening the WebSocket. Server reads it on /ws upgrade and, for connections without a current version, filters those two commands from keybinding payloads on serverGetConfig, upsert/remove responses, and subscribeServerConfig streams—without changing persisted config.

Regression tests cover unnegotiated, legacy (0), current, and future client versions across config, streams, and mutations.

Reviewed by Cursor Bugbot for commit 8785f3d. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Filter keybinding commands for older clients after new keybinding additions

  • Adds WS_KEYBINDING_COMMAND_SET_QUERY_PARAM and CURRENT_KEYBINDING_COMMAND_SET_VERSION (1) to rpc.ts as a versioning mechanism for keybinding command sets.
  • Clients advertise support by appending keybindingCommandSet=<version> to the WebSocket URL in session.ts.
  • The server in ws.ts inspects this query param and, for clients not advertising the current version, strips filePicker.toggle and projectSearch.toggle from keybindings in serverGetConfig, serverUpsertKeybinding, serverRemoveKeybinding, and subscribeServerConfig responses.
  • Behavioral Change: older or unversioned clients now receive a filtered keybinding set that omits the two new commands.

Macroscope summarized 8785f3d.

@coderabbitai

coderabbitaiBot commented Jul 30, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 54035889-0c45-499e-b0a8-f7d09bcb58c8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Jul 30, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Jul 30, 2026
@macroscopeapp

macroscopeappBot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This backwards-compatibility fix modifies what keybindings are sent to websocket clients based on version negotiation. While the change is well-scoped and thoroughly tested, it touches core server-client communication in ws.ts and the author is not a frequent contributor to this file, warranting human verification.

You can customize Macroscope's approvability policy. Learn more.

@macroscopeapp
macroscopeappBot dismissed their stale reviewJuly 30, 2026 18:29

Dismissing prior approval to re-evaluate 8785f3d

@NikAtNight

Copy link
Copy Markdown

Confirmed this exact regression against production T3 Connect. With Nightly .958, both the iOS app (T3Code/18) and app.t3.codes successfully authenticated and upgraded the WebSocket, then disconnected immediately after server.getConfig. Rolling the desktop environment back to .953 restored connectivity for both clients. This matches the closed-schema/keybinding compatibility failure described here.

@juliusmarminge

Copy link
Copy Markdown
Member

this feels like the wrong solution to the problem

@PixPMusic

Copy link
Copy Markdown
ContributorAuthor

@juliusmarminge main concern was keeping the nightly server compatible with the released mobile client, as App Store review time can take a while.

If you don't care about nightlies being compatible--or want to start doing CI nightlies for Android at least?--then we can move in another direction.

@juliusmarminge

Copy link
Copy Markdown
Member

I do care about nightlies being compatible, but we've already submitted a new version for review and I'm not sure if we can do a new OTA right now ( I think build versions are in a rough state...) so we should just fix it properly

@juliusmarminge

Copy link
Copy Markdown
Member

#5055

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@PixPMusic@NikAtNight@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(server): keep older clients connected after keybinding additions - #5018

Closed
PixPMusic wants to merge 2 commits into
pingdotgg:mainfrom
PixPMusic:fix/keybinding-wire-compat
Closed

fix(server): keep older clients connected after keybinding additions#5018
PixPMusic wants to merge 2 commits into
pingdotgg:mainfrom
PixPMusic:fix/keybinding-wire-compat

Conversation

@PixPMusic

@PixPMusicPixPMusic commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Negotiate the supported keybinding command set once per WebSocket connection.
  • Keep filePicker.toggle and projectSearch.toggle out of config and mutation responses for clients that predate the negotiation.
  • Preserve the full keybinding list for current web, desktop, and mobile clients.
  • Cover initial config, streamed config updates, and keybinding upsert/remove responses.

Why

Nightly 0.0.32-nightly.20260730.957 began returning two new default keybinding commands. The 0.0.31 client validates those commands against a closed schema while establishing its connection, so it rejects the initial server config and never reaches the connected state.

Current clients now advertise support through the WebSocket URL. Older clients omit the capability and receive a legacy-safe wire projection without changing the persisted or in-memory keybinding configuration. Older servers ignore the additional query parameter.

Verification

  • vp test run packages/client-runtime/src/rpc/session.test.ts apps/server/src/server.test.ts — 121 tests passed
  • vp run --filter @t3tools/contracts --filter @t3tools/client-runtime --filter t3 typecheck
  • targeted lint and formatting checks for all changed files
  • git diff --check origin/main...HEAD

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I added regression coverage

Model: GPT-5.6 Sol | Harness: Codex in T3 Code


Note

Medium Risk
Wire compatibility logic on the hot WebSocket path; mistakes could hide keybindings from current clients or still break legacy connects, but scope is limited to response projection with strong test coverage.

Overview
Adds WebSocket keybinding command-set negotiation so nightly servers can expose filePicker.toggle and projectSearch.toggle without breaking 0.0.31 clients that validate keybindings against a smaller schema during connect.

Contracts define WS_KEYBINDING_COMMAND_SET_QUERY_PARAM and CURRENT_KEYBINDING_COMMAND_SET_VERSION. Client runtime appends that query param when opening the WebSocket. Server reads it on /ws upgrade and, for connections without a current version, filters those two commands from keybinding payloads on serverGetConfig, upsert/remove responses, and subscribeServerConfig streams—without changing persisted config.

Regression tests cover unnegotiated, legacy (0), current, and future client versions across config, streams, and mutations.

Reviewed by Cursor Bugbot for commit 8785f3d. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Filter keybinding commands for older clients after new keybinding additions

  • Adds WS_KEYBINDING_COMMAND_SET_QUERY_PARAM and CURRENT_KEYBINDING_COMMAND_SET_VERSION (1) to rpc.ts as a versioning mechanism for keybinding command sets.
  • Clients advertise support by appending keybindingCommandSet=<version> to the WebSocket URL in session.ts.
  • The server in ws.ts inspects this query param and, for clients not advertising the current version, strips filePicker.toggle and projectSearch.toggle from keybindings in serverGetConfig, serverUpsertKeybinding, serverRemoveKeybinding, and subscribeServerConfig responses.
  • Behavioral Change: older or unversioned clients now receive a filtered keybinding set that omits the two new commands.

Macroscope summarized 8785f3d.

@coderabbitai

coderabbitaiBot commented Jul 30, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 54035889-0c45-499e-b0a8-f7d09bcb58c8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Jul 30, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Jul 30, 2026
@macroscopeapp

macroscopeappBot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This backwards-compatibility fix modifies what keybindings are sent to websocket clients based on version negotiation. While the change is well-scoped and thoroughly tested, it touches core server-client communication in ws.ts and the author is not a frequent contributor to this file, warranting human verification.

You can customize Macroscope's approvability policy. Learn more.

@macroscopeapp
macroscopeappBot dismissed their stale reviewJuly 30, 2026 18:29

Dismissing prior approval to re-evaluate 8785f3d

@NikAtNight

Copy link
Copy Markdown

Confirmed this exact regression against production T3 Connect. With Nightly .958, both the iOS app (T3Code/18) and app.t3.codes successfully authenticated and upgraded the WebSocket, then disconnected immediately after server.getConfig. Rolling the desktop environment back to .953 restored connectivity for both clients. This matches the closed-schema/keybinding compatibility failure described here.

@juliusmarminge

Copy link
Copy Markdown
Member

this feels like the wrong solution to the problem

@PixPMusic

Copy link
Copy Markdown
ContributorAuthor

@juliusmarminge main concern was keeping the nightly server compatible with the released mobile client, as App Store review time can take a while.

If you don't care about nightlies being compatible--or want to start doing CI nightlies for Android at least?--then we can move in another direction.

@juliusmarminge

Copy link
Copy Markdown
Member

I do care about nightlies being compatible, but we've already submitted a new version for review and I'm not sure if we can do a new OTA right now ( I think build versions are in a rough state...) so we should just fix it properly

@juliusmarminge

Copy link
Copy Markdown
Member

#5055

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@PixPMusic@NikAtNight@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(server): keep older clients connected after keybinding additions - #5018

Closed
PixPMusic wants to merge 2 commits into
pingdotgg:mainfrom
PixPMusic:fix/keybinding-wire-compat
Closed

fix(server): keep older clients connected after keybinding additions#5018
PixPMusic wants to merge 2 commits into
pingdotgg:mainfrom
PixPMusic:fix/keybinding-wire-compat

Conversation

@PixPMusic

@PixPMusicPixPMusic commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Negotiate the supported keybinding command set once per WebSocket connection.
  • Keep filePicker.toggle and projectSearch.toggle out of config and mutation responses for clients that predate the negotiation.
  • Preserve the full keybinding list for current web, desktop, and mobile clients.
  • Cover initial config, streamed config updates, and keybinding upsert/remove responses.

Why

Nightly 0.0.32-nightly.20260730.957 began returning two new default keybinding commands. The 0.0.31 client validates those commands against a closed schema while establishing its connection, so it rejects the initial server config and never reaches the connected state.

Current clients now advertise support through the WebSocket URL. Older clients omit the capability and receive a legacy-safe wire projection without changing the persisted or in-memory keybinding configuration. Older servers ignore the additional query parameter.

Verification

  • vp test run packages/client-runtime/src/rpc/session.test.ts apps/server/src/server.test.ts — 121 tests passed
  • vp run --filter @t3tools/contracts --filter @t3tools/client-runtime --filter t3 typecheck
  • targeted lint and formatting checks for all changed files
  • git diff --check origin/main...HEAD

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I added regression coverage

Model: GPT-5.6 Sol | Harness: Codex in T3 Code


Note

Medium Risk
Wire compatibility logic on the hot WebSocket path; mistakes could hide keybindings from current clients or still break legacy connects, but scope is limited to response projection with strong test coverage.

Overview
Adds WebSocket keybinding command-set negotiation so nightly servers can expose filePicker.toggle and projectSearch.toggle without breaking 0.0.31 clients that validate keybindings against a smaller schema during connect.

Contracts define WS_KEYBINDING_COMMAND_SET_QUERY_PARAM and CURRENT_KEYBINDING_COMMAND_SET_VERSION. Client runtime appends that query param when opening the WebSocket. Server reads it on /ws upgrade and, for connections without a current version, filters those two commands from keybinding payloads on serverGetConfig, upsert/remove responses, and subscribeServerConfig streams—without changing persisted config.

Regression tests cover unnegotiated, legacy (0), current, and future client versions across config, streams, and mutations.

Reviewed by Cursor Bugbot for commit 8785f3d. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Filter keybinding commands for older clients after new keybinding additions

  • Adds WS_KEYBINDING_COMMAND_SET_QUERY_PARAM and CURRENT_KEYBINDING_COMMAND_SET_VERSION (1) to rpc.ts as a versioning mechanism for keybinding command sets.
  • Clients advertise support by appending keybindingCommandSet=<version> to the WebSocket URL in session.ts.
  • The server in ws.ts inspects this query param and, for clients not advertising the current version, strips filePicker.toggle and projectSearch.toggle from keybindings in serverGetConfig, serverUpsertKeybinding, serverRemoveKeybinding, and subscribeServerConfig responses.
  • Behavioral Change: older or unversioned clients now receive a filtered keybinding set that omits the two new commands.

Macroscope summarized 8785f3d.

@coderabbitai

coderabbitaiBot commented Jul 30, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 54035889-0c45-499e-b0a8-f7d09bcb58c8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Jul 30, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Jul 30, 2026
@macroscopeapp

macroscopeappBot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This backwards-compatibility fix modifies what keybindings are sent to websocket clients based on version negotiation. While the change is well-scoped and thoroughly tested, it touches core server-client communication in ws.ts and the author is not a frequent contributor to this file, warranting human verification.

You can customize Macroscope's approvability policy. Learn more.

@macroscopeapp
macroscopeappBot dismissed their stale reviewJuly 30, 2026 18:29

Dismissing prior approval to re-evaluate 8785f3d

@NikAtNight

Copy link
Copy Markdown

Confirmed this exact regression against production T3 Connect. With Nightly .958, both the iOS app (T3Code/18) and app.t3.codes successfully authenticated and upgraded the WebSocket, then disconnected immediately after server.getConfig. Rolling the desktop environment back to .953 restored connectivity for both clients. This matches the closed-schema/keybinding compatibility failure described here.

@juliusmarminge

Copy link
Copy Markdown
Member

this feels like the wrong solution to the problem

@PixPMusic

Copy link
Copy Markdown
ContributorAuthor

@juliusmarminge main concern was keeping the nightly server compatible with the released mobile client, as App Store review time can take a while.

If you don't care about nightlies being compatible--or want to start doing CI nightlies for Android at least?--then we can move in another direction.

@juliusmarminge

Copy link
Copy Markdown
Member

I do care about nightlies being compatible, but we've already submitted a new version for review and I'm not sure if we can do a new OTA right now ( I think build versions are in a rough state...) so we should just fix it properly

@juliusmarminge

Copy link
Copy Markdown
Member

#5055

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@PixPMusic@NikAtNight@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(server): keep older clients connected after keybinding additions - #5018

Closed
PixPMusic wants to merge 2 commits into
pingdotgg:mainfrom
PixPMusic:fix/keybinding-wire-compat
Closed

fix(server): keep older clients connected after keybinding additions#5018
PixPMusic wants to merge 2 commits into
pingdotgg:mainfrom
PixPMusic:fix/keybinding-wire-compat

Conversation

@PixPMusic

@PixPMusicPixPMusic commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Negotiate the supported keybinding command set once per WebSocket connection.
  • Keep filePicker.toggle and projectSearch.toggle out of config and mutation responses for clients that predate the negotiation.
  • Preserve the full keybinding list for current web, desktop, and mobile clients.
  • Cover initial config, streamed config updates, and keybinding upsert/remove responses.

Why

Nightly 0.0.32-nightly.20260730.957 began returning two new default keybinding commands. The 0.0.31 client validates those commands against a closed schema while establishing its connection, so it rejects the initial server config and never reaches the connected state.

Current clients now advertise support through the WebSocket URL. Older clients omit the capability and receive a legacy-safe wire projection without changing the persisted or in-memory keybinding configuration. Older servers ignore the additional query parameter.

Verification

  • vp test run packages/client-runtime/src/rpc/session.test.ts apps/server/src/server.test.ts — 121 tests passed
  • vp run --filter @t3tools/contracts --filter @t3tools/client-runtime --filter t3 typecheck
  • targeted lint and formatting checks for all changed files
  • git diff --check origin/main...HEAD

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I added regression coverage

Model: GPT-5.6 Sol | Harness: Codex in T3 Code


Note

Medium Risk
Wire compatibility logic on the hot WebSocket path; mistakes could hide keybindings from current clients or still break legacy connects, but scope is limited to response projection with strong test coverage.

Overview
Adds WebSocket keybinding command-set negotiation so nightly servers can expose filePicker.toggle and projectSearch.toggle without breaking 0.0.31 clients that validate keybindings against a smaller schema during connect.

Contracts define WS_KEYBINDING_COMMAND_SET_QUERY_PARAM and CURRENT_KEYBINDING_COMMAND_SET_VERSION. Client runtime appends that query param when opening the WebSocket. Server reads it on /ws upgrade and, for connections without a current version, filters those two commands from keybinding payloads on serverGetConfig, upsert/remove responses, and subscribeServerConfig streams—without changing persisted config.

Regression tests cover unnegotiated, legacy (0), current, and future client versions across config, streams, and mutations.

Reviewed by Cursor Bugbot for commit 8785f3d. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Filter keybinding commands for older clients after new keybinding additions

  • Adds WS_KEYBINDING_COMMAND_SET_QUERY_PARAM and CURRENT_KEYBINDING_COMMAND_SET_VERSION (1) to rpc.ts as a versioning mechanism for keybinding command sets.
  • Clients advertise support by appending keybindingCommandSet=<version> to the WebSocket URL in session.ts.
  • The server in ws.ts inspects this query param and, for clients not advertising the current version, strips filePicker.toggle and projectSearch.toggle from keybindings in serverGetConfig, serverUpsertKeybinding, serverRemoveKeybinding, and subscribeServerConfig responses.
  • Behavioral Change: older or unversioned clients now receive a filtered keybinding set that omits the two new commands.

Macroscope summarized 8785f3d.

@coderabbitai

coderabbitaiBot commented Jul 30, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 54035889-0c45-499e-b0a8-f7d09bcb58c8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Jul 30, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Jul 30, 2026
@macroscopeapp

macroscopeappBot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This backwards-compatibility fix modifies what keybindings are sent to websocket clients based on version negotiation. While the change is well-scoped and thoroughly tested, it touches core server-client communication in ws.ts and the author is not a frequent contributor to this file, warranting human verification.

You can customize Macroscope's approvability policy. Learn more.

@macroscopeapp
macroscopeappBot dismissed their stale reviewJuly 30, 2026 18:29

Dismissing prior approval to re-evaluate 8785f3d

@NikAtNight

Copy link
Copy Markdown

Confirmed this exact regression against production T3 Connect. With Nightly .958, both the iOS app (T3Code/18) and app.t3.codes successfully authenticated and upgraded the WebSocket, then disconnected immediately after server.getConfig. Rolling the desktop environment back to .953 restored connectivity for both clients. This matches the closed-schema/keybinding compatibility failure described here.

@juliusmarminge

Copy link
Copy Markdown
Member

this feels like the wrong solution to the problem

@PixPMusic

Copy link
Copy Markdown
ContributorAuthor

@juliusmarminge main concern was keeping the nightly server compatible with the released mobile client, as App Store review time can take a while.

If you don't care about nightlies being compatible--or want to start doing CI nightlies for Android at least?--then we can move in another direction.

@juliusmarminge

Copy link
Copy Markdown
Member

I do care about nightlies being compatible, but we've already submitted a new version for review and I'm not sure if we can do a new OTA right now ( I think build versions are in a rough state...) so we should just fix it properly

@juliusmarminge

Copy link
Copy Markdown
Member

#5055

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@PixPMusic@NikAtNight@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(server): keep older clients connected after keybinding additions - #5018

Closed
PixPMusic wants to merge 2 commits into
pingdotgg:mainfrom
PixPMusic:fix/keybinding-wire-compat
Closed

fix(server): keep older clients connected after keybinding additions#5018
PixPMusic wants to merge 2 commits into
pingdotgg:mainfrom
PixPMusic:fix/keybinding-wire-compat

Conversation

@PixPMusic

@PixPMusicPixPMusic commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Negotiate the supported keybinding command set once per WebSocket connection.
  • Keep filePicker.toggle and projectSearch.toggle out of config and mutation responses for clients that predate the negotiation.
  • Preserve the full keybinding list for current web, desktop, and mobile clients.
  • Cover initial config, streamed config updates, and keybinding upsert/remove responses.

Why

Nightly 0.0.32-nightly.20260730.957 began returning two new default keybinding commands. The 0.0.31 client validates those commands against a closed schema while establishing its connection, so it rejects the initial server config and never reaches the connected state.

Current clients now advertise support through the WebSocket URL. Older clients omit the capability and receive a legacy-safe wire projection without changing the persisted or in-memory keybinding configuration. Older servers ignore the additional query parameter.

Verification

  • vp test run packages/client-runtime/src/rpc/session.test.ts apps/server/src/server.test.ts — 121 tests passed
  • vp run --filter @t3tools/contracts --filter @t3tools/client-runtime --filter t3 typecheck
  • targeted lint and formatting checks for all changed files
  • git diff --check origin/main...HEAD

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I added regression coverage

Model: GPT-5.6 Sol | Harness: Codex in T3 Code


Note

Medium Risk
Wire compatibility logic on the hot WebSocket path; mistakes could hide keybindings from current clients or still break legacy connects, but scope is limited to response projection with strong test coverage.

Overview
Adds WebSocket keybinding command-set negotiation so nightly servers can expose filePicker.toggle and projectSearch.toggle without breaking 0.0.31 clients that validate keybindings against a smaller schema during connect.

Contracts define WS_KEYBINDING_COMMAND_SET_QUERY_PARAM and CURRENT_KEYBINDING_COMMAND_SET_VERSION. Client runtime appends that query param when opening the WebSocket. Server reads it on /ws upgrade and, for connections without a current version, filters those two commands from keybinding payloads on serverGetConfig, upsert/remove responses, and subscribeServerConfig streams—without changing persisted config.

Regression tests cover unnegotiated, legacy (0), current, and future client versions across config, streams, and mutations.

Reviewed by Cursor Bugbot for commit 8785f3d. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Filter keybinding commands for older clients after new keybinding additions

  • Adds WS_KEYBINDING_COMMAND_SET_QUERY_PARAM and CURRENT_KEYBINDING_COMMAND_SET_VERSION (1) to rpc.ts as a versioning mechanism for keybinding command sets.
  • Clients advertise support by appending keybindingCommandSet=<version> to the WebSocket URL in session.ts.
  • The server in ws.ts inspects this query param and, for clients not advertising the current version, strips filePicker.toggle and projectSearch.toggle from keybindings in serverGetConfig, serverUpsertKeybinding, serverRemoveKeybinding, and subscribeServerConfig responses.
  • Behavioral Change: older or unversioned clients now receive a filtered keybinding set that omits the two new commands.

Macroscope summarized 8785f3d.

@coderabbitai

coderabbitaiBot commented Jul 30, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 54035889-0c45-499e-b0a8-f7d09bcb58c8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Jul 30, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Jul 30, 2026
@macroscopeapp

macroscopeappBot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This backwards-compatibility fix modifies what keybindings are sent to websocket clients based on version negotiation. While the change is well-scoped and thoroughly tested, it touches core server-client communication in ws.ts and the author is not a frequent contributor to this file, warranting human verification.

You can customize Macroscope's approvability policy. Learn more.

@macroscopeapp
macroscopeappBot dismissed their stale reviewJuly 30, 2026 18:29

Dismissing prior approval to re-evaluate 8785f3d

@NikAtNight

Copy link
Copy Markdown

Confirmed this exact regression against production T3 Connect. With Nightly .958, both the iOS app (T3Code/18) and app.t3.codes successfully authenticated and upgraded the WebSocket, then disconnected immediately after server.getConfig. Rolling the desktop environment back to .953 restored connectivity for both clients. This matches the closed-schema/keybinding compatibility failure described here.

@juliusmarminge

Copy link
Copy Markdown
Member

this feels like the wrong solution to the problem

@PixPMusic

Copy link
Copy Markdown
ContributorAuthor

@juliusmarminge main concern was keeping the nightly server compatible with the released mobile client, as App Store review time can take a while.

If you don't care about nightlies being compatible--or want to start doing CI nightlies for Android at least?--then we can move in another direction.

@juliusmarminge

Copy link
Copy Markdown
Member

I do care about nightlies being compatible, but we've already submitted a new version for review and I'm not sure if we can do a new OTA right now ( I think build versions are in a rough state...) so we should just fix it properly

@juliusmarminge

Copy link
Copy Markdown
Member

#5055

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@PixPMusic@NikAtNight@juliusmarminge