fix(contracts): tolerate unknown keybinding commands on client decode - #238

Closed
bulgadev wants to merge 89 commits into
patroza:fork/changesfrom
bulgadev:vscode-keybinding-fwdcompat
Closed

fix(contracts): tolerate unknown keybinding commands on client decode#238
bulgadev wants to merge 89 commits into
patroza:fork/changesfrom
bulgadev:vscode-keybinding-fwdcompat

Conversation

@bulgadev

Copy link
Copy Markdown

Problem

When a T3 Code server introduces new keybinding commands (e.g. filePicker.toggle, projectSearch.toggle), clients built against an older contracts build fail to decode the entire server.getConfig response because ResolvedKeybindingsConfig uses a closed union (KeybindingCommand) that rejects unknown values.

This manifested in the VS Code extension as:

T3 Code pairing failed: Could not connect to T3 Code: Expected "sidebar.toggle" | "terminal.toggle" | ... | "thread.jump.9", got "filePicker.toggle" at ["value"]["keybindings"][42]["command"]

The pairing itself succeeded (bearer token exchanged and stored), but the subsequent getConfig RPC failed during schema validation, blocking the connection.

Solution

Make the decode side of ResolvedKeybindingsConfig forward-compatible: rules whose command does not match the known union are silently dropped on decode, while known rules round-trip unchanged. Encoding is unaffected, so the server (which only ever emits known commands) and the strict authoring path (KeybindingRule / KeybindingsConfig) keep their exact behavior.

This is the same forward-compat pattern used elsewhere in the codebase (e.g. ProviderOptionSelections in model.ts tolerates legacy shapes via Schema.decodeTo + transformOrFail).

Scope

ResolvedKeybindingsConfig is consumed by all four wire schemas that carry resolved keybindings:

  • server.getConfig (initial config fetch)
  • subscribeServerConfig (config stream)
  • serverUpsertKeybinding / serverRemoveKeybinding (mutation results)

All three clients (vscode, web, mobile) share the same RpcClient.make(WsRpcGroup) decode path, so this fix unblocks all of them simultaneously.

Changes

  • packages/contracts/src/keybindings.ts: wrap ResolvedKeybindingsConfig with a decodeTo transform that filters unknown rules via Schema.decodeUnknownOption(ResolvedKeybindingRule) per element.
  • packages/contracts/src/keybindings.test.ts: two new tests verifying unknown commands are dropped and all-unknown arrays decode as empty.

Verification

  • pnpm --filter @t3tools/contracts test: 238 tests passed (19 files)
  • pnpm exec vp check packages/contracts/src/keybindings.ts packages/contracts/src/keybindings.test.ts: 0 errors, 0 warnings
  • ELECTRON_SKIP_BINARY_DOWNLOAD=1 pnpm exec vp run -r --cache --log labeled typecheck: exit 0

tim-smartand others added 30 commits July 30, 2026 18:43
Add custom "Open with" applications
Source: tim-smart#4
Source head: 8c4bdfbc5b57f6b600233244d330f9efa41dc498
Source commits: 08e1a4fb949585c3c441d6d00455fe904f72cd7b,cd43a401c6c148f1fe26cff72104ac527ea189f3,a8370e7502c552ebb064436e42e1c00f86f0946b,8c4bdfbc5b57f6b600233244d330f9efa41dc498
Imported: complete product delta from the source PR.
(cherry picked from commit 9fae005)
Load direnv environments for provider sessions
Source: tim-smart#5
Source head: 8f5fc87c13f4628c179cda44d4f32f7fe4d316b2
Source commits: e4f07014d39964fde2498bcb35588974cc5e6232,0d1463af61e0bd174f698b2519ebf3b207a2eaca,a66e4160d5f4b79140ec8fbcbc6aa66af750a991,8f5fc87c13f4628c179cda44d4f32f7fe4d316b2
Imported: complete product delta from the source PR.
(cherry picked from commit 0da8bfe)
Add unsigned retry for commit signing failures
Source: tim-smart#6
Source head: 7d65c5a224e97a6b811b0a84892f1fda065c5963
Source commits: 18ee567ecfdb11c9372153127b26b5cf57213a76,72a6fae23c86708080c4fed346d5bf0f136f0221,6614b28239ed2330a8f601357a413f2d50da195a,ec169369daa554541511aa28f551b36f3dd26485,7d65c5a224e97a6b811b0a84892f1fda065c5963
Imported: complete product delta from the source PR.
(cherry picked from commit 03671a2)
Add /new command for contextual threads
Source: tim-smart#7
Source head: 2051a8003041fe2806fcb4bc7a0d8940579fc543
Source commits: 2051a8003041fe2806fcb4bc7a0d8940579fc543
Imported: complete product delta from the source PR.
(cherry picked from commit 4d94f31)
Add session dashboard board
Source: tim-smart#8
Source head: d9f8e4d0a8dc22231ca315f3c595c3597f3b13e5
Source commits: 268fb8df9863ffbda51b975a8dbe68f11c41500c,dde20f271f674da22dd8f3a08201c2acf5e58ee5,df4a145e7b2cd2dc17a7a595267d2d8eb0a2a3f0,ce5723ddb0bf630a18d4cb8227b5344d12626e72,ad8c1a6af41161e1fc38a52f681b306517c7b918,6281887e6125317da0c7b4252d59bfd41c9bf35e,550db6316c634febdbe1cb27334d1347c23c7b2a,d9f8e4d0a8dc22231ca315f3c595c3597f3b13e5
Imported: complete product delta from the source PR.
(cherry picked from commit cd0e281)
Recover interrupted provider turns after server restarts
Source: tim-smart#9
Source head: b181832560177250b90bbfe07b0882c9e5b93493
Source commits: 7f69028a25be21f1882ecba14b62f387ad60cf2a,1d52bce1376766d804ef884d7d50b8b6d1b48cf7,b181832560177250b90bbfe07b0882c9e5b93493
Imported: complete product delta from the source PR.
(cherry picked from commit 83de8f5)
Avoid repeated thread snapshot loads during subscription retries
Source: tim-smart#10
Source head: c8c9eadb9de3026706bc3a403ca05b12d0da8dd5
Source commits: c8c9eadb9de3026706bc3a403ca05b12d0da8dd5
Imported: complete product delta from the source PR.
(cherry picked from commit 9e400c3)
Add image upload button to compact chat composer
Source: tim-smart#11
Source head: 1ff63f9b9c418ef56a46c6422d22c01de97581a8
Source commits: 1ff63f9b9c418ef56a46c6422d22c01de97581a8
Imported: complete product delta from the source PR.
(cherry picked from commit 720ec65)
Truncate mobile branch toolbar controls
Source: tim-smart#12
Source head: 1b7d44428472511bc98d8f936654359ce2536901
Source commits: 1b7d44428472511bc98d8f936654359ce2536901
Imported: complete product delta from the source PR.
(cherry picked from commit dc2bbb4)
Clean up worktrees when archiving threads
Source: tim-smart#13
Source head: a23f42d6ac671ea36b8db5d03934c089a31be448
Source commits: 4a194707ed134f993502ac5fdf36a8425f1769cd,1b6688aa5b641010cb2e9dad23d36d87257403ad,9ed32aa3923fb674380564b1ffcb3268290069b9,a23f42d6ac671ea36b8db5d03934c089a31be448
Imported: complete product delta from the source PR.
(cherry picked from commit 7e02dc9)
Pass hosted app channel into Vercel web builds
Source: tim-smart#14
Source head: de6966a6784b4703145c20b84fc482703bca4fa2
Source commits: de6966a6784b4703145c20b84fc482703bca4fa2
Imported: complete product delta from the source PR.
(cherry picked from commit 6333d8d)
Allow worktrees to reuse the selected branch
Source: tim-smart#15
Source head: 2d3900ba36c9397dc4fbe879c613a809f6b45384
Source commits: cd60531253fbafc470f5a5ac18d3e44832d3376d,2d3900ba36c9397dc4fbe879c613a809f6b45384
Imported: complete product delta from the source PR.
(cherry picked from commit 5e7dff2)
Add optional worktree removal confirmation
Source: tim-smart#16
Source head: c3f509fe8f690b704bb34692d9c132c0644db777
Source commits: 76f063e983ca3c39b20f79d8ea83783ab034251a,c3f509fe8f690b704bb34692d9c132c0644db777
Imported: complete product delta from the source PR.
(cherry picked from commit 9886109)
Stop retrying unavailable thread subscriptions
Source: tim-smart#17
Source head: 1359af8ba0b146e3d49f89b72c250f681e86199d
Source commits: 1359af8ba0b146e3d49f89b72c250f681e86199d
Imported: complete product delta from the source PR.
(cherry picked from commit 7b37a7a)
Compatibility fix for running the selected Tim stack on the fork CI matrix. Source adaptation review: patroza#31.
(cherry picked from commit 6edd39a)
Keep the selected Tim Open With feature portable on non-macOS builders and avoid treating custom app definitions as macOS bundles. Source adaptation review: patroza#33.
(cherry picked from commit 15a7d2a)
…nd; green tip
Bring Tim layer tip to typecheck green by joining main ref-refresh VCS client
state with fork failureKind/worktree-cleanup contracts, restoring
filterBrowseEntries/reuse-base-branch surfaces Tim dropped, and fixing
ChatView/Board call-site type errors left by incomplete Tim joins.
(cherry picked from commit 0e24917)
Bring fork/tim typecheck/test green after main pingdotgg#2679 + Tim client-runtime
rewrite: rejoin EnvironmentSubscriptionRpcTag/localApi/ws scopes, wire
BackgroundPolicy/ResourceTelemetry layers, force openpgp for signing tests
on hosts with gpg.format=ssh, and treat TRACE2 child_exit without
child_class as hook finish (git 2.55+).
…troza#29)
Source: pingdotgg#4018
Source SHA: de8fd65
Imported: bounded server activity snapshots, cursor pagination, lazy web history loading, reconnect-safe reset/dedup, and disabled eager browser sidebar hydration.
Adapted: preserved Tim thread lifecycle handling and Omega composer/minimap behavior while resolving current-stack conflicts.
Excluded: none of the source PR behavior; native mobile pagination remains separate because pingdotgg#4018 intentionally excludes it.
…#3510) (patroza#35)
Source: pingdotgg#3510
Source SHA: 034f4936d7a1435887bb62ac3f2db61f08928cbf
Imported: native mobile lazy loading for older thread activity, a 1,000-event subscription catch-up ceiling with snapshot fallback, and synchronized stale snapshot watermarks.
Adapted: applied above the refreshed pingdotgg#4018 web/server candidate and preserved Tim lifecycle handling plus our mobile composer changes.
Excluded: pingdotgg#3510 server/web pagination duplicated by pingdotgg#4018, the later shared-hook refactor, formatting-only commits, and contract comments. The shared refactor can be revisited independently after production validation.
…oza#34)
Source: pingdotgg#4176
Source SHA: 56b6615
Imported: O(1) command read-model maps, deleted-thread eviction, VCS cache cleanup, browser surface cleanup, preview idle TTL, and per-thread UI cleanup.
Adapted: preserved our thread settlement, snooze, and sequential worktree deletion actions while wiring upstream cleanup into the current hook.
Excluded: none.
Co-authored-by: Rusiru Sadathana <27785781+RusiruSadathana@users.noreply.github.com>
patroza#44)
Source: pingdotgg#4506
Source SHA: f7eaa00
Imported unchanged as one candidate provenance commit.
…tgg#4558)
Imported from https://github.com/pingdotgg/t3code/pull/4558\n\nAdapted to retain our provider restart-recovery constants while replacing the local default-title check with the shared policy.
After rebasing candidates onto the green tim tip, restore missing
EnvironmentThread loading fields, ChatView sendDisabledReason/threadSyncPhase
wiring, and orchestration.getThreadActivities auth coverage.
@patroza

Copy link
Copy Markdown
Owner

Thanks a lot for the contribution!

Requesting changes:

  • decodeUnknownOption(ResolvedKeybindingRule) drops every invalid rule, not only rules with unknown commands. Malformed rules using known commands should continue to fail validation; only genuinely unknown command names should be skipped.
  • The maximum-length check runs after filtering. A large input containing unknown or malformed rules can decode to an empty array and bypass the 256-rule limit. Please enforce the limit on the input array before filtering.

@patroza
patrozaforce-pushed the fork/changes branch 23 times, most recently from 2d1f67b to 271c4b2CompareAugust 5, 2026 14:38
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Ports [#237](#237) by
[@bulgadev](https://github.com/bulgadev) onto `fork/dev`.
The original targets the now-frozen `fork/vscode` overlay branch and
lives in a fork this repository
cannot push to, so it could not be rebased in place.
## Attribution
The commit is **authored by `bulgadev <me@bulgaaw.com>`**, not merely
credited — `git cherry-pick`
preserved it and the amend kept it. `Co-authored-by` is added on top as
insurance: `fork/dev` is
squash-only, and a squash can rewrite the author while trailers survive
in the body either way.
Please **merge this rather than #237**, and close#237 pointing here.
## Content
Adds a *T3 Code: Pair with Server…* command accepting a full pairing URL
(`http://host:port/pair#token=…`) or a bare pairing token, exchanges it
for a bearer access token via
the OAuth token-exchange endpoint, and stores it in `SecretStorage` so
`ensureConnected` picks it up
as the bearer tier. Removes the need to hand-exchange tokens with `curl`
before using *Set Server
Bearer Token*.
Unchanged from the original — 5 files, cherry-picked cleanly onto
`fork/dev` with no conflicts.
## Validation
- `apps/vscode` suite: **13 files, 44 tests pass**.
- Full recursive typecheck clean across all 17 packages.
## Note on the other external PR
[#238](#238) is **not** ported,
because it is already fixed on
`fork/dev` by a different route and porting it would duplicate the
behaviour:
- `ResolvedKeybindingsConfig` already uses the generic
`ForwardCompatibleArray` helper, whose decode
keeps only elements that decode and encodes unchanged — identical
semantics to the bespoke
`filterKnownResolvedKeybindingRules` the PR introduces.
- `fork/dev`'s tests are **strictly broader**: unknown commands, unknown
`when`-node types, and
malformed entries. #238 covers only the first.
- One of #238's assertions would now **fail**: it asserts
`filePicker.toggle` decodes away as
unknown, but that command has since shipped and is known.
Recommend closing #238 as already fixed, with credit to @bulgadev for
reporting the class of bug.
Co-authored by [@patroza](https://github.com/patroza),
[@bulgadev](https://github.com/bulgadev)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: bulgadev <me@bulgaaw.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
@omegent-app

Copy link
Copy Markdown

Thanks for this one too, @bulgadev — you were right about the bug, and it is fixed. Closing because
fork/dev already has it, arrived at independently.

ResolvedKeybindingsConfig now goes through a generic ForwardCompatibleArray helper:

exportconstResolvedKeybindingsConfig=ForwardCompatibleArray(ResolvedKeybindingRule).check(Schema.isMaxLength(MAX_KEYBINDINGS_COUNT),);

It decodes each element and keeps only the ones that decode, passing encode through untouched —
the same semantics as your filterKnownResolvedKeybindingRules, generalised so other contracts get
it for free. Merging yours on top would add a second, bespoke implementation of behaviour already in
place.

The test coverage that landed with it is also a superset of the two cases here:

  • rules whose command this build does not know — your case
  • rules with unknown when-node types
  • malformed entries ("garbage", null)

One thing worth flagging, since it says something about how long this sat: your second assertion
would now fail. It expects filePicker.toggle to decode away as unknown, but that command shipped in
the meantime and is in the known set — so a payload containing it decodes to one rule, not zero. The
class of bug you found is exactly why that matters, and the generic fix covers it.

For context on why this went quiet rather than getting reviewed: it targeted fork/changes, which
has since been frozen. This repository moved to a stable, never-rebased fork/dev as the contributor
target — partly because PRs like yours were being silently invalidated by rebases underneath them.
Your other PR (#237) is merged as #351 with your authorship on the commit.

fork/dev is the branch to target from here. Thanks again. 🙏

@omegent-appomegent-appBot closed this Aug 6, 2026
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Records the `fork/dev` development and release model — **adopted and
live since 2026-08-06**. This
started as a proposal; the migration then ran ahead of it, so the
document is now the record rather
than the plan.
Documentation only. Every mechanism it describes is already merged and
running.
## The model
`fork/dev` is the default branch, the contributor target and the release
source. It is never rebased.
The provenance stack `main → fork/base → fork/tim → fork/candidates`
stays rebased and feeds
`fork/dev` through reviewed tree deltas, so contributor bases are never
invalidated by an upstream
update.
| In place | |
| --- | --- |
| `fork/dev` cut from green `fork/integration` `21badd04e`, trees proven
identical | tag `fork-dev/2026-08-06.1` |
| Default branch, ruleset, squash-only, required checks | live |
| CI for `fork/dev` PRs and merges | #343 |
| Deployment promoting exact green `fork/dev` SHAs | ops `deploy.env` |
| Validation and release split | #347, #349 |
| First provenance sync, upstream `2a04db134..a2ca89a` | #345, tag
`fork-dev/2026-08-06.2` |
| Upstream ancestry recorded so "behind" reads true | `3a7e7a458` |
| Overlays drained and deregistered | #348 |
## What this revision corrects
The document had drifted from what was actually built:
- **Release is two workflows, not one.** `fork-ci` decides whether a SHA
is valid; `fork-release`
acts on that verdict via `workflow_run`. A release action must never be
able to veto a validation
verdict — when mobile dispatch lived inside `fork-ci`, one failed EAS
call marked a valid SHA
unapprovable and stranded the whole fleet.
- **Check selection is *not* path-inferred**, and the document
previously implied it should be. Every
PR runs all four required checks; only *release* scope is classified. A
path filter that errs
narrow silently skips a check on a protected branch, which is worse than
a slightly slower suite.
- **`fork/changes` and `fork/integration` are frozen**, not fallbacks.
- Ops parameterization and the `deploy.env` cutover are **done**, not
pending.
- Steps that were "do now" are recorded as done, with real SHAs, tags
and ruleset contents.
## What the cutover surfaced
Added as a section, because each cost a round trip and the old path hid
all of them:
- `fork/dev` had **no CI path at all** — no `push` trigger, not listed
as a `pull_request` base.
- **Mobile releases would have stopped silently**; nothing errors when a
gated job just never fires.
- Both mobile workflows **hardcoded `ref: fork/integration`** and
rejected every `fork/dev` SHA.
- A release failure could **strand the fleet**.
- **Every PR based on `fork/changes` was already broken** by earlier
rebases — GitHub reported them
as 60–100 commits and 629–741 files. Each was one commit of real work on
stale history, fixed by
cherry-picking that commit rather than replaying the branch.
That last one is the clearest evidence for the whole premise: the old
model was silently corrupting
in-flight work, and nobody could see it.
## Deliberately not done
Clean downstream projection is deferred indefinitely and nothing depends
on it. Provenance sync stays
manual. The overlay machinery is still present and still passes its
tests with an empty manifest;
removing it touches ~20 files and is a separate decision.
## Still open
PRs #317, #226 and #185 conflict when cherry-picked onto `fork/dev`;
#237 and #238 live in an
external fork and need their author. `fork/changes` and
`fork/integration` can be deleted once those
are drained.
## Also: no guidance targets an overlay any more
The overlays were drained in #348, but the instructions an agent or
contributor actually reads before
opening a PR still sent them at `fork/discord`, `fork/vscode`,
`fork/identity`, the desktop
deep-links branch, or `fork/changes`. Left alone, the next client-owned
change would have been opened
against a **closed overlay on a frozen branch**.
- **`CLAUDE.md`** (`AGENTS.md` symlinks to it): branch from and target
`fork/dev` for every kind of
work; `main`, `fork/changes` and `fork/integration` named as bases never
to use; the
"register an `integrationOverlays` entry" instructions replaced with a
record that it is empty.
- **`apps/discord-bot/docs/agent-turn-rules.md`**: recovery branches
pointed at *"the correct base
(`fork/discord` overlay / `fork/changes` / etc.)"* → `fork/dev`.
- **`fork-stack.md`, `stack-ship-path.md`, `client-overlays.md`**:
bannered as superseded rather than
rewritten — the provenance stack they document is still current and they
are the record of how the
fork worked before the cutover. The two lines that literally instructed
a base are corrected.
Verified by grep: nothing in the repository still directs a PR anywhere
but `fork/dev`.
## Validation
`vp fmt --check` clean; internal anchors checked. Documentation only —
no code, tooling or workflow
changes in this PR.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@bulgadev@patroza@tim-smart
, '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(contracts): tolerate unknown keybinding commands on client decode - #238

Closed
bulgadev wants to merge 89 commits into
patroza:fork/changesfrom
bulgadev:vscode-keybinding-fwdcompat
Closed

fix(contracts): tolerate unknown keybinding commands on client decode#238
bulgadev wants to merge 89 commits into
patroza:fork/changesfrom
bulgadev:vscode-keybinding-fwdcompat

Conversation

@bulgadev

Copy link
Copy Markdown

Problem

When a T3 Code server introduces new keybinding commands (e.g. filePicker.toggle, projectSearch.toggle), clients built against an older contracts build fail to decode the entire server.getConfig response because ResolvedKeybindingsConfig uses a closed union (KeybindingCommand) that rejects unknown values.

This manifested in the VS Code extension as:

T3 Code pairing failed: Could not connect to T3 Code: Expected "sidebar.toggle" | "terminal.toggle" | ... | "thread.jump.9", got "filePicker.toggle" at ["value"]["keybindings"][42]["command"]

The pairing itself succeeded (bearer token exchanged and stored), but the subsequent getConfig RPC failed during schema validation, blocking the connection.

Solution

Make the decode side of ResolvedKeybindingsConfig forward-compatible: rules whose command does not match the known union are silently dropped on decode, while known rules round-trip unchanged. Encoding is unaffected, so the server (which only ever emits known commands) and the strict authoring path (KeybindingRule / KeybindingsConfig) keep their exact behavior.

This is the same forward-compat pattern used elsewhere in the codebase (e.g. ProviderOptionSelections in model.ts tolerates legacy shapes via Schema.decodeTo + transformOrFail).

Scope

ResolvedKeybindingsConfig is consumed by all four wire schemas that carry resolved keybindings:

  • server.getConfig (initial config fetch)
  • subscribeServerConfig (config stream)
  • serverUpsertKeybinding / serverRemoveKeybinding (mutation results)

All three clients (vscode, web, mobile) share the same RpcClient.make(WsRpcGroup) decode path, so this fix unblocks all of them simultaneously.

Changes

  • packages/contracts/src/keybindings.ts: wrap ResolvedKeybindingsConfig with a decodeTo transform that filters unknown rules via Schema.decodeUnknownOption(ResolvedKeybindingRule) per element.
  • packages/contracts/src/keybindings.test.ts: two new tests verifying unknown commands are dropped and all-unknown arrays decode as empty.

Verification

  • pnpm --filter @t3tools/contracts test: 238 tests passed (19 files)
  • pnpm exec vp check packages/contracts/src/keybindings.ts packages/contracts/src/keybindings.test.ts: 0 errors, 0 warnings
  • ELECTRON_SKIP_BINARY_DOWNLOAD=1 pnpm exec vp run -r --cache --log labeled typecheck: exit 0

tim-smartand others added 30 commits July 30, 2026 18:43
Add custom "Open with" applications
Source: tim-smart#4
Source head: 8c4bdfbc5b57f6b600233244d330f9efa41dc498
Source commits: 08e1a4fb949585c3c441d6d00455fe904f72cd7b,cd43a401c6c148f1fe26cff72104ac527ea189f3,a8370e7502c552ebb064436e42e1c00f86f0946b,8c4bdfbc5b57f6b600233244d330f9efa41dc498
Imported: complete product delta from the source PR.
(cherry picked from commit 9fae005)
Load direnv environments for provider sessions
Source: tim-smart#5
Source head: 8f5fc87c13f4628c179cda44d4f32f7fe4d316b2
Source commits: e4f07014d39964fde2498bcb35588974cc5e6232,0d1463af61e0bd174f698b2519ebf3b207a2eaca,a66e4160d5f4b79140ec8fbcbc6aa66af750a991,8f5fc87c13f4628c179cda44d4f32f7fe4d316b2
Imported: complete product delta from the source PR.
(cherry picked from commit 0da8bfe)
Add unsigned retry for commit signing failures
Source: tim-smart#6
Source head: 7d65c5a224e97a6b811b0a84892f1fda065c5963
Source commits: 18ee567ecfdb11c9372153127b26b5cf57213a76,72a6fae23c86708080c4fed346d5bf0f136f0221,6614b28239ed2330a8f601357a413f2d50da195a,ec169369daa554541511aa28f551b36f3dd26485,7d65c5a224e97a6b811b0a84892f1fda065c5963
Imported: complete product delta from the source PR.
(cherry picked from commit 03671a2)
Add /new command for contextual threads
Source: tim-smart#7
Source head: 2051a8003041fe2806fcb4bc7a0d8940579fc543
Source commits: 2051a8003041fe2806fcb4bc7a0d8940579fc543
Imported: complete product delta from the source PR.
(cherry picked from commit 4d94f31)
Add session dashboard board
Source: tim-smart#8
Source head: d9f8e4d0a8dc22231ca315f3c595c3597f3b13e5
Source commits: 268fb8df9863ffbda51b975a8dbe68f11c41500c,dde20f271f674da22dd8f3a08201c2acf5e58ee5,df4a145e7b2cd2dc17a7a595267d2d8eb0a2a3f0,ce5723ddb0bf630a18d4cb8227b5344d12626e72,ad8c1a6af41161e1fc38a52f681b306517c7b918,6281887e6125317da0c7b4252d59bfd41c9bf35e,550db6316c634febdbe1cb27334d1347c23c7b2a,d9f8e4d0a8dc22231ca315f3c595c3597f3b13e5
Imported: complete product delta from the source PR.
(cherry picked from commit cd0e281)
Recover interrupted provider turns after server restarts
Source: tim-smart#9
Source head: b181832560177250b90bbfe07b0882c9e5b93493
Source commits: 7f69028a25be21f1882ecba14b62f387ad60cf2a,1d52bce1376766d804ef884d7d50b8b6d1b48cf7,b181832560177250b90bbfe07b0882c9e5b93493
Imported: complete product delta from the source PR.
(cherry picked from commit 83de8f5)
Avoid repeated thread snapshot loads during subscription retries
Source: tim-smart#10
Source head: c8c9eadb9de3026706bc3a403ca05b12d0da8dd5
Source commits: c8c9eadb9de3026706bc3a403ca05b12d0da8dd5
Imported: complete product delta from the source PR.
(cherry picked from commit 9e400c3)
Add image upload button to compact chat composer
Source: tim-smart#11
Source head: 1ff63f9b9c418ef56a46c6422d22c01de97581a8
Source commits: 1ff63f9b9c418ef56a46c6422d22c01de97581a8
Imported: complete product delta from the source PR.
(cherry picked from commit 720ec65)
Truncate mobile branch toolbar controls
Source: tim-smart#12
Source head: 1b7d44428472511bc98d8f936654359ce2536901
Source commits: 1b7d44428472511bc98d8f936654359ce2536901
Imported: complete product delta from the source PR.
(cherry picked from commit dc2bbb4)
Clean up worktrees when archiving threads
Source: tim-smart#13
Source head: a23f42d6ac671ea36b8db5d03934c089a31be448
Source commits: 4a194707ed134f993502ac5fdf36a8425f1769cd,1b6688aa5b641010cb2e9dad23d36d87257403ad,9ed32aa3923fb674380564b1ffcb3268290069b9,a23f42d6ac671ea36b8db5d03934c089a31be448
Imported: complete product delta from the source PR.
(cherry picked from commit 7e02dc9)
Pass hosted app channel into Vercel web builds
Source: tim-smart#14
Source head: de6966a6784b4703145c20b84fc482703bca4fa2
Source commits: de6966a6784b4703145c20b84fc482703bca4fa2
Imported: complete product delta from the source PR.
(cherry picked from commit 6333d8d)
Allow worktrees to reuse the selected branch
Source: tim-smart#15
Source head: 2d3900ba36c9397dc4fbe879c613a809f6b45384
Source commits: cd60531253fbafc470f5a5ac18d3e44832d3376d,2d3900ba36c9397dc4fbe879c613a809f6b45384
Imported: complete product delta from the source PR.
(cherry picked from commit 5e7dff2)
Add optional worktree removal confirmation
Source: tim-smart#16
Source head: c3f509fe8f690b704bb34692d9c132c0644db777
Source commits: 76f063e983ca3c39b20f79d8ea83783ab034251a,c3f509fe8f690b704bb34692d9c132c0644db777
Imported: complete product delta from the source PR.
(cherry picked from commit 9886109)
Stop retrying unavailable thread subscriptions
Source: tim-smart#17
Source head: 1359af8ba0b146e3d49f89b72c250f681e86199d
Source commits: 1359af8ba0b146e3d49f89b72c250f681e86199d
Imported: complete product delta from the source PR.
(cherry picked from commit 7b37a7a)
Compatibility fix for running the selected Tim stack on the fork CI matrix. Source adaptation review: patroza#31.
(cherry picked from commit 6edd39a)
Keep the selected Tim Open With feature portable on non-macOS builders and avoid treating custom app definitions as macOS bundles. Source adaptation review: patroza#33.
(cherry picked from commit 15a7d2a)
…nd; green tip
Bring Tim layer tip to typecheck green by joining main ref-refresh VCS client
state with fork failureKind/worktree-cleanup contracts, restoring
filterBrowseEntries/reuse-base-branch surfaces Tim dropped, and fixing
ChatView/Board call-site type errors left by incomplete Tim joins.
(cherry picked from commit 0e24917)
Bring fork/tim typecheck/test green after main pingdotgg#2679 + Tim client-runtime
rewrite: rejoin EnvironmentSubscriptionRpcTag/localApi/ws scopes, wire
BackgroundPolicy/ResourceTelemetry layers, force openpgp for signing tests
on hosts with gpg.format=ssh, and treat TRACE2 child_exit without
child_class as hook finish (git 2.55+).
…troza#29)
Source: pingdotgg#4018
Source SHA: de8fd65
Imported: bounded server activity snapshots, cursor pagination, lazy web history loading, reconnect-safe reset/dedup, and disabled eager browser sidebar hydration.
Adapted: preserved Tim thread lifecycle handling and Omega composer/minimap behavior while resolving current-stack conflicts.
Excluded: none of the source PR behavior; native mobile pagination remains separate because pingdotgg#4018 intentionally excludes it.
…#3510) (patroza#35)
Source: pingdotgg#3510
Source SHA: 034f4936d7a1435887bb62ac3f2db61f08928cbf
Imported: native mobile lazy loading for older thread activity, a 1,000-event subscription catch-up ceiling with snapshot fallback, and synchronized stale snapshot watermarks.
Adapted: applied above the refreshed pingdotgg#4018 web/server candidate and preserved Tim lifecycle handling plus our mobile composer changes.
Excluded: pingdotgg#3510 server/web pagination duplicated by pingdotgg#4018, the later shared-hook refactor, formatting-only commits, and contract comments. The shared refactor can be revisited independently after production validation.
…oza#34)
Source: pingdotgg#4176
Source SHA: 56b6615
Imported: O(1) command read-model maps, deleted-thread eviction, VCS cache cleanup, browser surface cleanup, preview idle TTL, and per-thread UI cleanup.
Adapted: preserved our thread settlement, snooze, and sequential worktree deletion actions while wiring upstream cleanup into the current hook.
Excluded: none.
Co-authored-by: Rusiru Sadathana <27785781+RusiruSadathana@users.noreply.github.com>
patroza#44)
Source: pingdotgg#4506
Source SHA: f7eaa00
Imported unchanged as one candidate provenance commit.
…tgg#4558)
Imported from https://github.com/pingdotgg/t3code/pull/4558\n\nAdapted to retain our provider restart-recovery constants while replacing the local default-title check with the shared policy.
After rebasing candidates onto the green tim tip, restore missing
EnvironmentThread loading fields, ChatView sendDisabledReason/threadSyncPhase
wiring, and orchestration.getThreadActivities auth coverage.
@patroza

Copy link
Copy Markdown
Owner

Thanks a lot for the contribution!

Requesting changes:

  • decodeUnknownOption(ResolvedKeybindingRule) drops every invalid rule, not only rules with unknown commands. Malformed rules using known commands should continue to fail validation; only genuinely unknown command names should be skipped.
  • The maximum-length check runs after filtering. A large input containing unknown or malformed rules can decode to an empty array and bypass the 256-rule limit. Please enforce the limit on the input array before filtering.

@patroza
patrozaforce-pushed the fork/changes branch 23 times, most recently from 2d1f67b to 271c4b2CompareAugust 5, 2026 14:38
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Ports [#237](#237) by
[@bulgadev](https://github.com/bulgadev) onto `fork/dev`.
The original targets the now-frozen `fork/vscode` overlay branch and
lives in a fork this repository
cannot push to, so it could not be rebased in place.
## Attribution
The commit is **authored by `bulgadev <me@bulgaaw.com>`**, not merely
credited — `git cherry-pick`
preserved it and the amend kept it. `Co-authored-by` is added on top as
insurance: `fork/dev` is
squash-only, and a squash can rewrite the author while trailers survive
in the body either way.
Please **merge this rather than #237**, and close#237 pointing here.
## Content
Adds a *T3 Code: Pair with Server…* command accepting a full pairing URL
(`http://host:port/pair#token=…`) or a bare pairing token, exchanges it
for a bearer access token via
the OAuth token-exchange endpoint, and stores it in `SecretStorage` so
`ensureConnected` picks it up
as the bearer tier. Removes the need to hand-exchange tokens with `curl`
before using *Set Server
Bearer Token*.
Unchanged from the original — 5 files, cherry-picked cleanly onto
`fork/dev` with no conflicts.
## Validation
- `apps/vscode` suite: **13 files, 44 tests pass**.
- Full recursive typecheck clean across all 17 packages.
## Note on the other external PR
[#238](#238) is **not** ported,
because it is already fixed on
`fork/dev` by a different route and porting it would duplicate the
behaviour:
- `ResolvedKeybindingsConfig` already uses the generic
`ForwardCompatibleArray` helper, whose decode
keeps only elements that decode and encodes unchanged — identical
semantics to the bespoke
`filterKnownResolvedKeybindingRules` the PR introduces.
- `fork/dev`'s tests are **strictly broader**: unknown commands, unknown
`when`-node types, and
malformed entries. #238 covers only the first.
- One of #238's assertions would now **fail**: it asserts
`filePicker.toggle` decodes away as
unknown, but that command has since shipped and is known.
Recommend closing #238 as already fixed, with credit to @bulgadev for
reporting the class of bug.
Co-authored by [@patroza](https://github.com/patroza),
[@bulgadev](https://github.com/bulgadev)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: bulgadev <me@bulgaaw.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
@omegent-app

Copy link
Copy Markdown

Thanks for this one too, @bulgadev — you were right about the bug, and it is fixed. Closing because
fork/dev already has it, arrived at independently.

ResolvedKeybindingsConfig now goes through a generic ForwardCompatibleArray helper:

exportconstResolvedKeybindingsConfig=ForwardCompatibleArray(ResolvedKeybindingRule).check(Schema.isMaxLength(MAX_KEYBINDINGS_COUNT),);

It decodes each element and keeps only the ones that decode, passing encode through untouched —
the same semantics as your filterKnownResolvedKeybindingRules, generalised so other contracts get
it for free. Merging yours on top would add a second, bespoke implementation of behaviour already in
place.

The test coverage that landed with it is also a superset of the two cases here:

  • rules whose command this build does not know — your case
  • rules with unknown when-node types
  • malformed entries ("garbage", null)

One thing worth flagging, since it says something about how long this sat: your second assertion
would now fail. It expects filePicker.toggle to decode away as unknown, but that command shipped in
the meantime and is in the known set — so a payload containing it decodes to one rule, not zero. The
class of bug you found is exactly why that matters, and the generic fix covers it.

For context on why this went quiet rather than getting reviewed: it targeted fork/changes, which
has since been frozen. This repository moved to a stable, never-rebased fork/dev as the contributor
target — partly because PRs like yours were being silently invalidated by rebases underneath them.
Your other PR (#237) is merged as #351 with your authorship on the commit.

fork/dev is the branch to target from here. Thanks again. 🙏

@omegent-appomegent-appBot closed this Aug 6, 2026
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Records the `fork/dev` development and release model — **adopted and
live since 2026-08-06**. This
started as a proposal; the migration then ran ahead of it, so the
document is now the record rather
than the plan.
Documentation only. Every mechanism it describes is already merged and
running.
## The model
`fork/dev` is the default branch, the contributor target and the release
source. It is never rebased.
The provenance stack `main → fork/base → fork/tim → fork/candidates`
stays rebased and feeds
`fork/dev` through reviewed tree deltas, so contributor bases are never
invalidated by an upstream
update.
| In place | |
| --- | --- |
| `fork/dev` cut from green `fork/integration` `21badd04e`, trees proven
identical | tag `fork-dev/2026-08-06.1` |
| Default branch, ruleset, squash-only, required checks | live |
| CI for `fork/dev` PRs and merges | #343 |
| Deployment promoting exact green `fork/dev` SHAs | ops `deploy.env` |
| Validation and release split | #347, #349 |
| First provenance sync, upstream `2a04db134..a2ca89a` | #345, tag
`fork-dev/2026-08-06.2` |
| Upstream ancestry recorded so "behind" reads true | `3a7e7a458` |
| Overlays drained and deregistered | #348 |
## What this revision corrects
The document had drifted from what was actually built:
- **Release is two workflows, not one.** `fork-ci` decides whether a SHA
is valid; `fork-release`
acts on that verdict via `workflow_run`. A release action must never be
able to veto a validation
verdict — when mobile dispatch lived inside `fork-ci`, one failed EAS
call marked a valid SHA
unapprovable and stranded the whole fleet.
- **Check selection is *not* path-inferred**, and the document
previously implied it should be. Every
PR runs all four required checks; only *release* scope is classified. A
path filter that errs
narrow silently skips a check on a protected branch, which is worse than
a slightly slower suite.
- **`fork/changes` and `fork/integration` are frozen**, not fallbacks.
- Ops parameterization and the `deploy.env` cutover are **done**, not
pending.
- Steps that were "do now" are recorded as done, with real SHAs, tags
and ruleset contents.
## What the cutover surfaced
Added as a section, because each cost a round trip and the old path hid
all of them:
- `fork/dev` had **no CI path at all** — no `push` trigger, not listed
as a `pull_request` base.
- **Mobile releases would have stopped silently**; nothing errors when a
gated job just never fires.
- Both mobile workflows **hardcoded `ref: fork/integration`** and
rejected every `fork/dev` SHA.
- A release failure could **strand the fleet**.
- **Every PR based on `fork/changes` was already broken** by earlier
rebases — GitHub reported them
as 60–100 commits and 629–741 files. Each was one commit of real work on
stale history, fixed by
cherry-picking that commit rather than replaying the branch.
That last one is the clearest evidence for the whole premise: the old
model was silently corrupting
in-flight work, and nobody could see it.
## Deliberately not done
Clean downstream projection is deferred indefinitely and nothing depends
on it. Provenance sync stays
manual. The overlay machinery is still present and still passes its
tests with an empty manifest;
removing it touches ~20 files and is a separate decision.
## Still open
PRs #317, #226 and #185 conflict when cherry-picked onto `fork/dev`;
#237 and #238 live in an
external fork and need their author. `fork/changes` and
`fork/integration` can be deleted once those
are drained.
## Also: no guidance targets an overlay any more
The overlays were drained in #348, but the instructions an agent or
contributor actually reads before
opening a PR still sent them at `fork/discord`, `fork/vscode`,
`fork/identity`, the desktop
deep-links branch, or `fork/changes`. Left alone, the next client-owned
change would have been opened
against a **closed overlay on a frozen branch**.
- **`CLAUDE.md`** (`AGENTS.md` symlinks to it): branch from and target
`fork/dev` for every kind of
work; `main`, `fork/changes` and `fork/integration` named as bases never
to use; the
"register an `integrationOverlays` entry" instructions replaced with a
record that it is empty.
- **`apps/discord-bot/docs/agent-turn-rules.md`**: recovery branches
pointed at *"the correct base
(`fork/discord` overlay / `fork/changes` / etc.)"* → `fork/dev`.
- **`fork-stack.md`, `stack-ship-path.md`, `client-overlays.md`**:
bannered as superseded rather than
rewritten — the provenance stack they document is still current and they
are the record of how the
fork worked before the cutover. The two lines that literally instructed
a base are corrected.
Verified by grep: nothing in the repository still directs a PR anywhere
but `fork/dev`.
## Validation
`vp fmt --check` clean; internal anchors checked. Documentation only —
no code, tooling or workflow
changes in this PR.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@bulgadev@patroza@tim-smart
, '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(contracts): tolerate unknown keybinding commands on client decode - #238

Closed
bulgadev wants to merge 89 commits into
patroza:fork/changesfrom
bulgadev:vscode-keybinding-fwdcompat
Closed

fix(contracts): tolerate unknown keybinding commands on client decode#238
bulgadev wants to merge 89 commits into
patroza:fork/changesfrom
bulgadev:vscode-keybinding-fwdcompat

Conversation

@bulgadev

Copy link
Copy Markdown

Problem

When a T3 Code server introduces new keybinding commands (e.g. filePicker.toggle, projectSearch.toggle), clients built against an older contracts build fail to decode the entire server.getConfig response because ResolvedKeybindingsConfig uses a closed union (KeybindingCommand) that rejects unknown values.

This manifested in the VS Code extension as:

T3 Code pairing failed: Could not connect to T3 Code: Expected "sidebar.toggle" | "terminal.toggle" | ... | "thread.jump.9", got "filePicker.toggle" at ["value"]["keybindings"][42]["command"]

The pairing itself succeeded (bearer token exchanged and stored), but the subsequent getConfig RPC failed during schema validation, blocking the connection.

Solution

Make the decode side of ResolvedKeybindingsConfig forward-compatible: rules whose command does not match the known union are silently dropped on decode, while known rules round-trip unchanged. Encoding is unaffected, so the server (which only ever emits known commands) and the strict authoring path (KeybindingRule / KeybindingsConfig) keep their exact behavior.

This is the same forward-compat pattern used elsewhere in the codebase (e.g. ProviderOptionSelections in model.ts tolerates legacy shapes via Schema.decodeTo + transformOrFail).

Scope

ResolvedKeybindingsConfig is consumed by all four wire schemas that carry resolved keybindings:

  • server.getConfig (initial config fetch)
  • subscribeServerConfig (config stream)
  • serverUpsertKeybinding / serverRemoveKeybinding (mutation results)

All three clients (vscode, web, mobile) share the same RpcClient.make(WsRpcGroup) decode path, so this fix unblocks all of them simultaneously.

Changes

  • packages/contracts/src/keybindings.ts: wrap ResolvedKeybindingsConfig with a decodeTo transform that filters unknown rules via Schema.decodeUnknownOption(ResolvedKeybindingRule) per element.
  • packages/contracts/src/keybindings.test.ts: two new tests verifying unknown commands are dropped and all-unknown arrays decode as empty.

Verification

  • pnpm --filter @t3tools/contracts test: 238 tests passed (19 files)
  • pnpm exec vp check packages/contracts/src/keybindings.ts packages/contracts/src/keybindings.test.ts: 0 errors, 0 warnings
  • ELECTRON_SKIP_BINARY_DOWNLOAD=1 pnpm exec vp run -r --cache --log labeled typecheck: exit 0

tim-smartand others added 30 commits July 30, 2026 18:43
Add custom "Open with" applications
Source: tim-smart#4
Source head: 8c4bdfbc5b57f6b600233244d330f9efa41dc498
Source commits: 08e1a4fb949585c3c441d6d00455fe904f72cd7b,cd43a401c6c148f1fe26cff72104ac527ea189f3,a8370e7502c552ebb064436e42e1c00f86f0946b,8c4bdfbc5b57f6b600233244d330f9efa41dc498
Imported: complete product delta from the source PR.
(cherry picked from commit 9fae005)
Load direnv environments for provider sessions
Source: tim-smart#5
Source head: 8f5fc87c13f4628c179cda44d4f32f7fe4d316b2
Source commits: e4f07014d39964fde2498bcb35588974cc5e6232,0d1463af61e0bd174f698b2519ebf3b207a2eaca,a66e4160d5f4b79140ec8fbcbc6aa66af750a991,8f5fc87c13f4628c179cda44d4f32f7fe4d316b2
Imported: complete product delta from the source PR.
(cherry picked from commit 0da8bfe)
Add unsigned retry for commit signing failures
Source: tim-smart#6
Source head: 7d65c5a224e97a6b811b0a84892f1fda065c5963
Source commits: 18ee567ecfdb11c9372153127b26b5cf57213a76,72a6fae23c86708080c4fed346d5bf0f136f0221,6614b28239ed2330a8f601357a413f2d50da195a,ec169369daa554541511aa28f551b36f3dd26485,7d65c5a224e97a6b811b0a84892f1fda065c5963
Imported: complete product delta from the source PR.
(cherry picked from commit 03671a2)
Add /new command for contextual threads
Source: tim-smart#7
Source head: 2051a8003041fe2806fcb4bc7a0d8940579fc543
Source commits: 2051a8003041fe2806fcb4bc7a0d8940579fc543
Imported: complete product delta from the source PR.
(cherry picked from commit 4d94f31)
Add session dashboard board
Source: tim-smart#8
Source head: d9f8e4d0a8dc22231ca315f3c595c3597f3b13e5
Source commits: 268fb8df9863ffbda51b975a8dbe68f11c41500c,dde20f271f674da22dd8f3a08201c2acf5e58ee5,df4a145e7b2cd2dc17a7a595267d2d8eb0a2a3f0,ce5723ddb0bf630a18d4cb8227b5344d12626e72,ad8c1a6af41161e1fc38a52f681b306517c7b918,6281887e6125317da0c7b4252d59bfd41c9bf35e,550db6316c634febdbe1cb27334d1347c23c7b2a,d9f8e4d0a8dc22231ca315f3c595c3597f3b13e5
Imported: complete product delta from the source PR.
(cherry picked from commit cd0e281)
Recover interrupted provider turns after server restarts
Source: tim-smart#9
Source head: b181832560177250b90bbfe07b0882c9e5b93493
Source commits: 7f69028a25be21f1882ecba14b62f387ad60cf2a,1d52bce1376766d804ef884d7d50b8b6d1b48cf7,b181832560177250b90bbfe07b0882c9e5b93493
Imported: complete product delta from the source PR.
(cherry picked from commit 83de8f5)
Avoid repeated thread snapshot loads during subscription retries
Source: tim-smart#10
Source head: c8c9eadb9de3026706bc3a403ca05b12d0da8dd5
Source commits: c8c9eadb9de3026706bc3a403ca05b12d0da8dd5
Imported: complete product delta from the source PR.
(cherry picked from commit 9e400c3)
Add image upload button to compact chat composer
Source: tim-smart#11
Source head: 1ff63f9b9c418ef56a46c6422d22c01de97581a8
Source commits: 1ff63f9b9c418ef56a46c6422d22c01de97581a8
Imported: complete product delta from the source PR.
(cherry picked from commit 720ec65)
Truncate mobile branch toolbar controls
Source: tim-smart#12
Source head: 1b7d44428472511bc98d8f936654359ce2536901
Source commits: 1b7d44428472511bc98d8f936654359ce2536901
Imported: complete product delta from the source PR.
(cherry picked from commit dc2bbb4)
Clean up worktrees when archiving threads
Source: tim-smart#13
Source head: a23f42d6ac671ea36b8db5d03934c089a31be448
Source commits: 4a194707ed134f993502ac5fdf36a8425f1769cd,1b6688aa5b641010cb2e9dad23d36d87257403ad,9ed32aa3923fb674380564b1ffcb3268290069b9,a23f42d6ac671ea36b8db5d03934c089a31be448
Imported: complete product delta from the source PR.
(cherry picked from commit 7e02dc9)
Pass hosted app channel into Vercel web builds
Source: tim-smart#14
Source head: de6966a6784b4703145c20b84fc482703bca4fa2
Source commits: de6966a6784b4703145c20b84fc482703bca4fa2
Imported: complete product delta from the source PR.
(cherry picked from commit 6333d8d)
Allow worktrees to reuse the selected branch
Source: tim-smart#15
Source head: 2d3900ba36c9397dc4fbe879c613a809f6b45384
Source commits: cd60531253fbafc470f5a5ac18d3e44832d3376d,2d3900ba36c9397dc4fbe879c613a809f6b45384
Imported: complete product delta from the source PR.
(cherry picked from commit 5e7dff2)
Add optional worktree removal confirmation
Source: tim-smart#16
Source head: c3f509fe8f690b704bb34692d9c132c0644db777
Source commits: 76f063e983ca3c39b20f79d8ea83783ab034251a,c3f509fe8f690b704bb34692d9c132c0644db777
Imported: complete product delta from the source PR.
(cherry picked from commit 9886109)
Stop retrying unavailable thread subscriptions
Source: tim-smart#17
Source head: 1359af8ba0b146e3d49f89b72c250f681e86199d
Source commits: 1359af8ba0b146e3d49f89b72c250f681e86199d
Imported: complete product delta from the source PR.
(cherry picked from commit 7b37a7a)
Compatibility fix for running the selected Tim stack on the fork CI matrix. Source adaptation review: patroza#31.
(cherry picked from commit 6edd39a)
Keep the selected Tim Open With feature portable on non-macOS builders and avoid treating custom app definitions as macOS bundles. Source adaptation review: patroza#33.
(cherry picked from commit 15a7d2a)
…nd; green tip
Bring Tim layer tip to typecheck green by joining main ref-refresh VCS client
state with fork failureKind/worktree-cleanup contracts, restoring
filterBrowseEntries/reuse-base-branch surfaces Tim dropped, and fixing
ChatView/Board call-site type errors left by incomplete Tim joins.
(cherry picked from commit 0e24917)
Bring fork/tim typecheck/test green after main pingdotgg#2679 + Tim client-runtime
rewrite: rejoin EnvironmentSubscriptionRpcTag/localApi/ws scopes, wire
BackgroundPolicy/ResourceTelemetry layers, force openpgp for signing tests
on hosts with gpg.format=ssh, and treat TRACE2 child_exit without
child_class as hook finish (git 2.55+).
…troza#29)
Source: pingdotgg#4018
Source SHA: de8fd65
Imported: bounded server activity snapshots, cursor pagination, lazy web history loading, reconnect-safe reset/dedup, and disabled eager browser sidebar hydration.
Adapted: preserved Tim thread lifecycle handling and Omega composer/minimap behavior while resolving current-stack conflicts.
Excluded: none of the source PR behavior; native mobile pagination remains separate because pingdotgg#4018 intentionally excludes it.
…#3510) (patroza#35)
Source: pingdotgg#3510
Source SHA: 034f4936d7a1435887bb62ac3f2db61f08928cbf
Imported: native mobile lazy loading for older thread activity, a 1,000-event subscription catch-up ceiling with snapshot fallback, and synchronized stale snapshot watermarks.
Adapted: applied above the refreshed pingdotgg#4018 web/server candidate and preserved Tim lifecycle handling plus our mobile composer changes.
Excluded: pingdotgg#3510 server/web pagination duplicated by pingdotgg#4018, the later shared-hook refactor, formatting-only commits, and contract comments. The shared refactor can be revisited independently after production validation.
…oza#34)
Source: pingdotgg#4176
Source SHA: 56b6615
Imported: O(1) command read-model maps, deleted-thread eviction, VCS cache cleanup, browser surface cleanup, preview idle TTL, and per-thread UI cleanup.
Adapted: preserved our thread settlement, snooze, and sequential worktree deletion actions while wiring upstream cleanup into the current hook.
Excluded: none.
Co-authored-by: Rusiru Sadathana <27785781+RusiruSadathana@users.noreply.github.com>
patroza#44)
Source: pingdotgg#4506
Source SHA: f7eaa00
Imported unchanged as one candidate provenance commit.
…tgg#4558)
Imported from https://github.com/pingdotgg/t3code/pull/4558\n\nAdapted to retain our provider restart-recovery constants while replacing the local default-title check with the shared policy.
After rebasing candidates onto the green tim tip, restore missing
EnvironmentThread loading fields, ChatView sendDisabledReason/threadSyncPhase
wiring, and orchestration.getThreadActivities auth coverage.
@patroza

Copy link
Copy Markdown
Owner

Thanks a lot for the contribution!

Requesting changes:

  • decodeUnknownOption(ResolvedKeybindingRule) drops every invalid rule, not only rules with unknown commands. Malformed rules using known commands should continue to fail validation; only genuinely unknown command names should be skipped.
  • The maximum-length check runs after filtering. A large input containing unknown or malformed rules can decode to an empty array and bypass the 256-rule limit. Please enforce the limit on the input array before filtering.

@patroza
patrozaforce-pushed the fork/changes branch 23 times, most recently from 2d1f67b to 271c4b2CompareAugust 5, 2026 14:38
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Ports [#237](#237) by
[@bulgadev](https://github.com/bulgadev) onto `fork/dev`.
The original targets the now-frozen `fork/vscode` overlay branch and
lives in a fork this repository
cannot push to, so it could not be rebased in place.
## Attribution
The commit is **authored by `bulgadev <me@bulgaaw.com>`**, not merely
credited — `git cherry-pick`
preserved it and the amend kept it. `Co-authored-by` is added on top as
insurance: `fork/dev` is
squash-only, and a squash can rewrite the author while trailers survive
in the body either way.
Please **merge this rather than #237**, and close#237 pointing here.
## Content
Adds a *T3 Code: Pair with Server…* command accepting a full pairing URL
(`http://host:port/pair#token=…`) or a bare pairing token, exchanges it
for a bearer access token via
the OAuth token-exchange endpoint, and stores it in `SecretStorage` so
`ensureConnected` picks it up
as the bearer tier. Removes the need to hand-exchange tokens with `curl`
before using *Set Server
Bearer Token*.
Unchanged from the original — 5 files, cherry-picked cleanly onto
`fork/dev` with no conflicts.
## Validation
- `apps/vscode` suite: **13 files, 44 tests pass**.
- Full recursive typecheck clean across all 17 packages.
## Note on the other external PR
[#238](#238) is **not** ported,
because it is already fixed on
`fork/dev` by a different route and porting it would duplicate the
behaviour:
- `ResolvedKeybindingsConfig` already uses the generic
`ForwardCompatibleArray` helper, whose decode
keeps only elements that decode and encodes unchanged — identical
semantics to the bespoke
`filterKnownResolvedKeybindingRules` the PR introduces.
- `fork/dev`'s tests are **strictly broader**: unknown commands, unknown
`when`-node types, and
malformed entries. #238 covers only the first.
- One of #238's assertions would now **fail**: it asserts
`filePicker.toggle` decodes away as
unknown, but that command has since shipped and is known.
Recommend closing #238 as already fixed, with credit to @bulgadev for
reporting the class of bug.
Co-authored by [@patroza](https://github.com/patroza),
[@bulgadev](https://github.com/bulgadev)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: bulgadev <me@bulgaaw.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
@omegent-app

Copy link
Copy Markdown

Thanks for this one too, @bulgadev — you were right about the bug, and it is fixed. Closing because
fork/dev already has it, arrived at independently.

ResolvedKeybindingsConfig now goes through a generic ForwardCompatibleArray helper:

exportconstResolvedKeybindingsConfig=ForwardCompatibleArray(ResolvedKeybindingRule).check(Schema.isMaxLength(MAX_KEYBINDINGS_COUNT),);

It decodes each element and keeps only the ones that decode, passing encode through untouched —
the same semantics as your filterKnownResolvedKeybindingRules, generalised so other contracts get
it for free. Merging yours on top would add a second, bespoke implementation of behaviour already in
place.

The test coverage that landed with it is also a superset of the two cases here:

  • rules whose command this build does not know — your case
  • rules with unknown when-node types
  • malformed entries ("garbage", null)

One thing worth flagging, since it says something about how long this sat: your second assertion
would now fail. It expects filePicker.toggle to decode away as unknown, but that command shipped in
the meantime and is in the known set — so a payload containing it decodes to one rule, not zero. The
class of bug you found is exactly why that matters, and the generic fix covers it.

For context on why this went quiet rather than getting reviewed: it targeted fork/changes, which
has since been frozen. This repository moved to a stable, never-rebased fork/dev as the contributor
target — partly because PRs like yours were being silently invalidated by rebases underneath them.
Your other PR (#237) is merged as #351 with your authorship on the commit.

fork/dev is the branch to target from here. Thanks again. 🙏

@omegent-appomegent-appBot closed this Aug 6, 2026
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Records the `fork/dev` development and release model — **adopted and
live since 2026-08-06**. This
started as a proposal; the migration then ran ahead of it, so the
document is now the record rather
than the plan.
Documentation only. Every mechanism it describes is already merged and
running.
## The model
`fork/dev` is the default branch, the contributor target and the release
source. It is never rebased.
The provenance stack `main → fork/base → fork/tim → fork/candidates`
stays rebased and feeds
`fork/dev` through reviewed tree deltas, so contributor bases are never
invalidated by an upstream
update.
| In place | |
| --- | --- |
| `fork/dev` cut from green `fork/integration` `21badd04e`, trees proven
identical | tag `fork-dev/2026-08-06.1` |
| Default branch, ruleset, squash-only, required checks | live |
| CI for `fork/dev` PRs and merges | #343 |
| Deployment promoting exact green `fork/dev` SHAs | ops `deploy.env` |
| Validation and release split | #347, #349 |
| First provenance sync, upstream `2a04db134..a2ca89a` | #345, tag
`fork-dev/2026-08-06.2` |
| Upstream ancestry recorded so "behind" reads true | `3a7e7a458` |
| Overlays drained and deregistered | #348 |
## What this revision corrects
The document had drifted from what was actually built:
- **Release is two workflows, not one.** `fork-ci` decides whether a SHA
is valid; `fork-release`
acts on that verdict via `workflow_run`. A release action must never be
able to veto a validation
verdict — when mobile dispatch lived inside `fork-ci`, one failed EAS
call marked a valid SHA
unapprovable and stranded the whole fleet.
- **Check selection is *not* path-inferred**, and the document
previously implied it should be. Every
PR runs all four required checks; only *release* scope is classified. A
path filter that errs
narrow silently skips a check on a protected branch, which is worse than
a slightly slower suite.
- **`fork/changes` and `fork/integration` are frozen**, not fallbacks.
- Ops parameterization and the `deploy.env` cutover are **done**, not
pending.
- Steps that were "do now" are recorded as done, with real SHAs, tags
and ruleset contents.
## What the cutover surfaced
Added as a section, because each cost a round trip and the old path hid
all of them:
- `fork/dev` had **no CI path at all** — no `push` trigger, not listed
as a `pull_request` base.
- **Mobile releases would have stopped silently**; nothing errors when a
gated job just never fires.
- Both mobile workflows **hardcoded `ref: fork/integration`** and
rejected every `fork/dev` SHA.
- A release failure could **strand the fleet**.
- **Every PR based on `fork/changes` was already broken** by earlier
rebases — GitHub reported them
as 60–100 commits and 629–741 files. Each was one commit of real work on
stale history, fixed by
cherry-picking that commit rather than replaying the branch.
That last one is the clearest evidence for the whole premise: the old
model was silently corrupting
in-flight work, and nobody could see it.
## Deliberately not done
Clean downstream projection is deferred indefinitely and nothing depends
on it. Provenance sync stays
manual. The overlay machinery is still present and still passes its
tests with an empty manifest;
removing it touches ~20 files and is a separate decision.
## Still open
PRs #317, #226 and #185 conflict when cherry-picked onto `fork/dev`;
#237 and #238 live in an
external fork and need their author. `fork/changes` and
`fork/integration` can be deleted once those
are drained.
## Also: no guidance targets an overlay any more
The overlays were drained in #348, but the instructions an agent or
contributor actually reads before
opening a PR still sent them at `fork/discord`, `fork/vscode`,
`fork/identity`, the desktop
deep-links branch, or `fork/changes`. Left alone, the next client-owned
change would have been opened
against a **closed overlay on a frozen branch**.
- **`CLAUDE.md`** (`AGENTS.md` symlinks to it): branch from and target
`fork/dev` for every kind of
work; `main`, `fork/changes` and `fork/integration` named as bases never
to use; the
"register an `integrationOverlays` entry" instructions replaced with a
record that it is empty.
- **`apps/discord-bot/docs/agent-turn-rules.md`**: recovery branches
pointed at *"the correct base
(`fork/discord` overlay / `fork/changes` / etc.)"* → `fork/dev`.
- **`fork-stack.md`, `stack-ship-path.md`, `client-overlays.md`**:
bannered as superseded rather than
rewritten — the provenance stack they document is still current and they
are the record of how the
fork worked before the cutover. The two lines that literally instructed
a base are corrected.
Verified by grep: nothing in the repository still directs a PR anywhere
but `fork/dev`.
## Validation
`vp fmt --check` clean; internal anchors checked. Documentation only —
no code, tooling or workflow
changes in this PR.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@bulgadev@patroza@tim-smart
, '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(contracts): tolerate unknown keybinding commands on client decode - #238

Closed
bulgadev wants to merge 89 commits into
patroza:fork/changesfrom
bulgadev:vscode-keybinding-fwdcompat
Closed

fix(contracts): tolerate unknown keybinding commands on client decode#238
bulgadev wants to merge 89 commits into
patroza:fork/changesfrom
bulgadev:vscode-keybinding-fwdcompat

Conversation

@bulgadev

Copy link
Copy Markdown

Problem

When a T3 Code server introduces new keybinding commands (e.g. filePicker.toggle, projectSearch.toggle), clients built against an older contracts build fail to decode the entire server.getConfig response because ResolvedKeybindingsConfig uses a closed union (KeybindingCommand) that rejects unknown values.

This manifested in the VS Code extension as:

T3 Code pairing failed: Could not connect to T3 Code: Expected "sidebar.toggle" | "terminal.toggle" | ... | "thread.jump.9", got "filePicker.toggle" at ["value"]["keybindings"][42]["command"]

The pairing itself succeeded (bearer token exchanged and stored), but the subsequent getConfig RPC failed during schema validation, blocking the connection.

Solution

Make the decode side of ResolvedKeybindingsConfig forward-compatible: rules whose command does not match the known union are silently dropped on decode, while known rules round-trip unchanged. Encoding is unaffected, so the server (which only ever emits known commands) and the strict authoring path (KeybindingRule / KeybindingsConfig) keep their exact behavior.

This is the same forward-compat pattern used elsewhere in the codebase (e.g. ProviderOptionSelections in model.ts tolerates legacy shapes via Schema.decodeTo + transformOrFail).

Scope

ResolvedKeybindingsConfig is consumed by all four wire schemas that carry resolved keybindings:

  • server.getConfig (initial config fetch)
  • subscribeServerConfig (config stream)
  • serverUpsertKeybinding / serverRemoveKeybinding (mutation results)

All three clients (vscode, web, mobile) share the same RpcClient.make(WsRpcGroup) decode path, so this fix unblocks all of them simultaneously.

Changes

  • packages/contracts/src/keybindings.ts: wrap ResolvedKeybindingsConfig with a decodeTo transform that filters unknown rules via Schema.decodeUnknownOption(ResolvedKeybindingRule) per element.
  • packages/contracts/src/keybindings.test.ts: two new tests verifying unknown commands are dropped and all-unknown arrays decode as empty.

Verification

  • pnpm --filter @t3tools/contracts test: 238 tests passed (19 files)
  • pnpm exec vp check packages/contracts/src/keybindings.ts packages/contracts/src/keybindings.test.ts: 0 errors, 0 warnings
  • ELECTRON_SKIP_BINARY_DOWNLOAD=1 pnpm exec vp run -r --cache --log labeled typecheck: exit 0

tim-smartand others added 30 commits July 30, 2026 18:43
Add custom "Open with" applications
Source: tim-smart#4
Source head: 8c4bdfbc5b57f6b600233244d330f9efa41dc498
Source commits: 08e1a4fb949585c3c441d6d00455fe904f72cd7b,cd43a401c6c148f1fe26cff72104ac527ea189f3,a8370e7502c552ebb064436e42e1c00f86f0946b,8c4bdfbc5b57f6b600233244d330f9efa41dc498
Imported: complete product delta from the source PR.
(cherry picked from commit 9fae005)
Load direnv environments for provider sessions
Source: tim-smart#5
Source head: 8f5fc87c13f4628c179cda44d4f32f7fe4d316b2
Source commits: e4f07014d39964fde2498bcb35588974cc5e6232,0d1463af61e0bd174f698b2519ebf3b207a2eaca,a66e4160d5f4b79140ec8fbcbc6aa66af750a991,8f5fc87c13f4628c179cda44d4f32f7fe4d316b2
Imported: complete product delta from the source PR.
(cherry picked from commit 0da8bfe)
Add unsigned retry for commit signing failures
Source: tim-smart#6
Source head: 7d65c5a224e97a6b811b0a84892f1fda065c5963
Source commits: 18ee567ecfdb11c9372153127b26b5cf57213a76,72a6fae23c86708080c4fed346d5bf0f136f0221,6614b28239ed2330a8f601357a413f2d50da195a,ec169369daa554541511aa28f551b36f3dd26485,7d65c5a224e97a6b811b0a84892f1fda065c5963
Imported: complete product delta from the source PR.
(cherry picked from commit 03671a2)
Add /new command for contextual threads
Source: tim-smart#7
Source head: 2051a8003041fe2806fcb4bc7a0d8940579fc543
Source commits: 2051a8003041fe2806fcb4bc7a0d8940579fc543
Imported: complete product delta from the source PR.
(cherry picked from commit 4d94f31)
Add session dashboard board
Source: tim-smart#8
Source head: d9f8e4d0a8dc22231ca315f3c595c3597f3b13e5
Source commits: 268fb8df9863ffbda51b975a8dbe68f11c41500c,dde20f271f674da22dd8f3a08201c2acf5e58ee5,df4a145e7b2cd2dc17a7a595267d2d8eb0a2a3f0,ce5723ddb0bf630a18d4cb8227b5344d12626e72,ad8c1a6af41161e1fc38a52f681b306517c7b918,6281887e6125317da0c7b4252d59bfd41c9bf35e,550db6316c634febdbe1cb27334d1347c23c7b2a,d9f8e4d0a8dc22231ca315f3c595c3597f3b13e5
Imported: complete product delta from the source PR.
(cherry picked from commit cd0e281)
Recover interrupted provider turns after server restarts
Source: tim-smart#9
Source head: b181832560177250b90bbfe07b0882c9e5b93493
Source commits: 7f69028a25be21f1882ecba14b62f387ad60cf2a,1d52bce1376766d804ef884d7d50b8b6d1b48cf7,b181832560177250b90bbfe07b0882c9e5b93493
Imported: complete product delta from the source PR.
(cherry picked from commit 83de8f5)
Avoid repeated thread snapshot loads during subscription retries
Source: tim-smart#10
Source head: c8c9eadb9de3026706bc3a403ca05b12d0da8dd5
Source commits: c8c9eadb9de3026706bc3a403ca05b12d0da8dd5
Imported: complete product delta from the source PR.
(cherry picked from commit 9e400c3)
Add image upload button to compact chat composer
Source: tim-smart#11
Source head: 1ff63f9b9c418ef56a46c6422d22c01de97581a8
Source commits: 1ff63f9b9c418ef56a46c6422d22c01de97581a8
Imported: complete product delta from the source PR.
(cherry picked from commit 720ec65)
Truncate mobile branch toolbar controls
Source: tim-smart#12
Source head: 1b7d44428472511bc98d8f936654359ce2536901
Source commits: 1b7d44428472511bc98d8f936654359ce2536901
Imported: complete product delta from the source PR.
(cherry picked from commit dc2bbb4)
Clean up worktrees when archiving threads
Source: tim-smart#13
Source head: a23f42d6ac671ea36b8db5d03934c089a31be448
Source commits: 4a194707ed134f993502ac5fdf36a8425f1769cd,1b6688aa5b641010cb2e9dad23d36d87257403ad,9ed32aa3923fb674380564b1ffcb3268290069b9,a23f42d6ac671ea36b8db5d03934c089a31be448
Imported: complete product delta from the source PR.
(cherry picked from commit 7e02dc9)
Pass hosted app channel into Vercel web builds
Source: tim-smart#14
Source head: de6966a6784b4703145c20b84fc482703bca4fa2
Source commits: de6966a6784b4703145c20b84fc482703bca4fa2
Imported: complete product delta from the source PR.
(cherry picked from commit 6333d8d)
Allow worktrees to reuse the selected branch
Source: tim-smart#15
Source head: 2d3900ba36c9397dc4fbe879c613a809f6b45384
Source commits: cd60531253fbafc470f5a5ac18d3e44832d3376d,2d3900ba36c9397dc4fbe879c613a809f6b45384
Imported: complete product delta from the source PR.
(cherry picked from commit 5e7dff2)
Add optional worktree removal confirmation
Source: tim-smart#16
Source head: c3f509fe8f690b704bb34692d9c132c0644db777
Source commits: 76f063e983ca3c39b20f79d8ea83783ab034251a,c3f509fe8f690b704bb34692d9c132c0644db777
Imported: complete product delta from the source PR.
(cherry picked from commit 9886109)
Stop retrying unavailable thread subscriptions
Source: tim-smart#17
Source head: 1359af8ba0b146e3d49f89b72c250f681e86199d
Source commits: 1359af8ba0b146e3d49f89b72c250f681e86199d
Imported: complete product delta from the source PR.
(cherry picked from commit 7b37a7a)
Compatibility fix for running the selected Tim stack on the fork CI matrix. Source adaptation review: patroza#31.
(cherry picked from commit 6edd39a)
Keep the selected Tim Open With feature portable on non-macOS builders and avoid treating custom app definitions as macOS bundles. Source adaptation review: patroza#33.
(cherry picked from commit 15a7d2a)
…nd; green tip
Bring Tim layer tip to typecheck green by joining main ref-refresh VCS client
state with fork failureKind/worktree-cleanup contracts, restoring
filterBrowseEntries/reuse-base-branch surfaces Tim dropped, and fixing
ChatView/Board call-site type errors left by incomplete Tim joins.
(cherry picked from commit 0e24917)
Bring fork/tim typecheck/test green after main pingdotgg#2679 + Tim client-runtime
rewrite: rejoin EnvironmentSubscriptionRpcTag/localApi/ws scopes, wire
BackgroundPolicy/ResourceTelemetry layers, force openpgp for signing tests
on hosts with gpg.format=ssh, and treat TRACE2 child_exit without
child_class as hook finish (git 2.55+).
…troza#29)
Source: pingdotgg#4018
Source SHA: de8fd65
Imported: bounded server activity snapshots, cursor pagination, lazy web history loading, reconnect-safe reset/dedup, and disabled eager browser sidebar hydration.
Adapted: preserved Tim thread lifecycle handling and Omega composer/minimap behavior while resolving current-stack conflicts.
Excluded: none of the source PR behavior; native mobile pagination remains separate because pingdotgg#4018 intentionally excludes it.
…#3510) (patroza#35)
Source: pingdotgg#3510
Source SHA: 034f4936d7a1435887bb62ac3f2db61f08928cbf
Imported: native mobile lazy loading for older thread activity, a 1,000-event subscription catch-up ceiling with snapshot fallback, and synchronized stale snapshot watermarks.
Adapted: applied above the refreshed pingdotgg#4018 web/server candidate and preserved Tim lifecycle handling plus our mobile composer changes.
Excluded: pingdotgg#3510 server/web pagination duplicated by pingdotgg#4018, the later shared-hook refactor, formatting-only commits, and contract comments. The shared refactor can be revisited independently after production validation.
…oza#34)
Source: pingdotgg#4176
Source SHA: 56b6615
Imported: O(1) command read-model maps, deleted-thread eviction, VCS cache cleanup, browser surface cleanup, preview idle TTL, and per-thread UI cleanup.
Adapted: preserved our thread settlement, snooze, and sequential worktree deletion actions while wiring upstream cleanup into the current hook.
Excluded: none.
Co-authored-by: Rusiru Sadathana <27785781+RusiruSadathana@users.noreply.github.com>
patroza#44)
Source: pingdotgg#4506
Source SHA: f7eaa00
Imported unchanged as one candidate provenance commit.
…tgg#4558)
Imported from https://github.com/pingdotgg/t3code/pull/4558\n\nAdapted to retain our provider restart-recovery constants while replacing the local default-title check with the shared policy.
After rebasing candidates onto the green tim tip, restore missing
EnvironmentThread loading fields, ChatView sendDisabledReason/threadSyncPhase
wiring, and orchestration.getThreadActivities auth coverage.
@patroza

Copy link
Copy Markdown
Owner

Thanks a lot for the contribution!

Requesting changes:

  • decodeUnknownOption(ResolvedKeybindingRule) drops every invalid rule, not only rules with unknown commands. Malformed rules using known commands should continue to fail validation; only genuinely unknown command names should be skipped.
  • The maximum-length check runs after filtering. A large input containing unknown or malformed rules can decode to an empty array and bypass the 256-rule limit. Please enforce the limit on the input array before filtering.

@patroza
patrozaforce-pushed the fork/changes branch 23 times, most recently from 2d1f67b to 271c4b2CompareAugust 5, 2026 14:38
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Ports [#237](#237) by
[@bulgadev](https://github.com/bulgadev) onto `fork/dev`.
The original targets the now-frozen `fork/vscode` overlay branch and
lives in a fork this repository
cannot push to, so it could not be rebased in place.
## Attribution
The commit is **authored by `bulgadev <me@bulgaaw.com>`**, not merely
credited — `git cherry-pick`
preserved it and the amend kept it. `Co-authored-by` is added on top as
insurance: `fork/dev` is
squash-only, and a squash can rewrite the author while trailers survive
in the body either way.
Please **merge this rather than #237**, and close#237 pointing here.
## Content
Adds a *T3 Code: Pair with Server…* command accepting a full pairing URL
(`http://host:port/pair#token=…`) or a bare pairing token, exchanges it
for a bearer access token via
the OAuth token-exchange endpoint, and stores it in `SecretStorage` so
`ensureConnected` picks it up
as the bearer tier. Removes the need to hand-exchange tokens with `curl`
before using *Set Server
Bearer Token*.
Unchanged from the original — 5 files, cherry-picked cleanly onto
`fork/dev` with no conflicts.
## Validation
- `apps/vscode` suite: **13 files, 44 tests pass**.
- Full recursive typecheck clean across all 17 packages.
## Note on the other external PR
[#238](#238) is **not** ported,
because it is already fixed on
`fork/dev` by a different route and porting it would duplicate the
behaviour:
- `ResolvedKeybindingsConfig` already uses the generic
`ForwardCompatibleArray` helper, whose decode
keeps only elements that decode and encodes unchanged — identical
semantics to the bespoke
`filterKnownResolvedKeybindingRules` the PR introduces.
- `fork/dev`'s tests are **strictly broader**: unknown commands, unknown
`when`-node types, and
malformed entries. #238 covers only the first.
- One of #238's assertions would now **fail**: it asserts
`filePicker.toggle` decodes away as
unknown, but that command has since shipped and is known.
Recommend closing #238 as already fixed, with credit to @bulgadev for
reporting the class of bug.
Co-authored by [@patroza](https://github.com/patroza),
[@bulgadev](https://github.com/bulgadev)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: bulgadev <me@bulgaaw.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
@omegent-app

Copy link
Copy Markdown

Thanks for this one too, @bulgadev — you were right about the bug, and it is fixed. Closing because
fork/dev already has it, arrived at independently.

ResolvedKeybindingsConfig now goes through a generic ForwardCompatibleArray helper:

exportconstResolvedKeybindingsConfig=ForwardCompatibleArray(ResolvedKeybindingRule).check(Schema.isMaxLength(MAX_KEYBINDINGS_COUNT),);

It decodes each element and keeps only the ones that decode, passing encode through untouched —
the same semantics as your filterKnownResolvedKeybindingRules, generalised so other contracts get
it for free. Merging yours on top would add a second, bespoke implementation of behaviour already in
place.

The test coverage that landed with it is also a superset of the two cases here:

  • rules whose command this build does not know — your case
  • rules with unknown when-node types
  • malformed entries ("garbage", null)

One thing worth flagging, since it says something about how long this sat: your second assertion
would now fail. It expects filePicker.toggle to decode away as unknown, but that command shipped in
the meantime and is in the known set — so a payload containing it decodes to one rule, not zero. The
class of bug you found is exactly why that matters, and the generic fix covers it.

For context on why this went quiet rather than getting reviewed: it targeted fork/changes, which
has since been frozen. This repository moved to a stable, never-rebased fork/dev as the contributor
target — partly because PRs like yours were being silently invalidated by rebases underneath them.
Your other PR (#237) is merged as #351 with your authorship on the commit.

fork/dev is the branch to target from here. Thanks again. 🙏

@omegent-appomegent-appBot closed this Aug 6, 2026
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Records the `fork/dev` development and release model — **adopted and
live since 2026-08-06**. This
started as a proposal; the migration then ran ahead of it, so the
document is now the record rather
than the plan.
Documentation only. Every mechanism it describes is already merged and
running.
## The model
`fork/dev` is the default branch, the contributor target and the release
source. It is never rebased.
The provenance stack `main → fork/base → fork/tim → fork/candidates`
stays rebased and feeds
`fork/dev` through reviewed tree deltas, so contributor bases are never
invalidated by an upstream
update.
| In place | |
| --- | --- |
| `fork/dev` cut from green `fork/integration` `21badd04e`, trees proven
identical | tag `fork-dev/2026-08-06.1` |
| Default branch, ruleset, squash-only, required checks | live |
| CI for `fork/dev` PRs and merges | #343 |
| Deployment promoting exact green `fork/dev` SHAs | ops `deploy.env` |
| Validation and release split | #347, #349 |
| First provenance sync, upstream `2a04db134..a2ca89a` | #345, tag
`fork-dev/2026-08-06.2` |
| Upstream ancestry recorded so "behind" reads true | `3a7e7a458` |
| Overlays drained and deregistered | #348 |
## What this revision corrects
The document had drifted from what was actually built:
- **Release is two workflows, not one.** `fork-ci` decides whether a SHA
is valid; `fork-release`
acts on that verdict via `workflow_run`. A release action must never be
able to veto a validation
verdict — when mobile dispatch lived inside `fork-ci`, one failed EAS
call marked a valid SHA
unapprovable and stranded the whole fleet.
- **Check selection is *not* path-inferred**, and the document
previously implied it should be. Every
PR runs all four required checks; only *release* scope is classified. A
path filter that errs
narrow silently skips a check on a protected branch, which is worse than
a slightly slower suite.
- **`fork/changes` and `fork/integration` are frozen**, not fallbacks.
- Ops parameterization and the `deploy.env` cutover are **done**, not
pending.
- Steps that were "do now" are recorded as done, with real SHAs, tags
and ruleset contents.
## What the cutover surfaced
Added as a section, because each cost a round trip and the old path hid
all of them:
- `fork/dev` had **no CI path at all** — no `push` trigger, not listed
as a `pull_request` base.
- **Mobile releases would have stopped silently**; nothing errors when a
gated job just never fires.
- Both mobile workflows **hardcoded `ref: fork/integration`** and
rejected every `fork/dev` SHA.
- A release failure could **strand the fleet**.
- **Every PR based on `fork/changes` was already broken** by earlier
rebases — GitHub reported them
as 60–100 commits and 629–741 files. Each was one commit of real work on
stale history, fixed by
cherry-picking that commit rather than replaying the branch.
That last one is the clearest evidence for the whole premise: the old
model was silently corrupting
in-flight work, and nobody could see it.
## Deliberately not done
Clean downstream projection is deferred indefinitely and nothing depends
on it. Provenance sync stays
manual. The overlay machinery is still present and still passes its
tests with an empty manifest;
removing it touches ~20 files and is a separate decision.
## Still open
PRs #317, #226 and #185 conflict when cherry-picked onto `fork/dev`;
#237 and #238 live in an
external fork and need their author. `fork/changes` and
`fork/integration` can be deleted once those
are drained.
## Also: no guidance targets an overlay any more
The overlays were drained in #348, but the instructions an agent or
contributor actually reads before
opening a PR still sent them at `fork/discord`, `fork/vscode`,
`fork/identity`, the desktop
deep-links branch, or `fork/changes`. Left alone, the next client-owned
change would have been opened
against a **closed overlay on a frozen branch**.
- **`CLAUDE.md`** (`AGENTS.md` symlinks to it): branch from and target
`fork/dev` for every kind of
work; `main`, `fork/changes` and `fork/integration` named as bases never
to use; the
"register an `integrationOverlays` entry" instructions replaced with a
record that it is empty.
- **`apps/discord-bot/docs/agent-turn-rules.md`**: recovery branches
pointed at *"the correct base
(`fork/discord` overlay / `fork/changes` / etc.)"* → `fork/dev`.
- **`fork-stack.md`, `stack-ship-path.md`, `client-overlays.md`**:
bannered as superseded rather than
rewritten — the provenance stack they document is still current and they
are the record of how the
fork worked before the cutover. The two lines that literally instructed
a base are corrected.
Verified by grep: nothing in the repository still directs a PR anywhere
but `fork/dev`.
## Validation
`vp fmt --check` clean; internal anchors checked. Documentation only —
no code, tooling or workflow
changes in this PR.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@bulgadev@patroza@tim-smart
, '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(contracts): tolerate unknown keybinding commands on client decode - #238

Closed
bulgadev wants to merge 89 commits into
patroza:fork/changesfrom
bulgadev:vscode-keybinding-fwdcompat
Closed

fix(contracts): tolerate unknown keybinding commands on client decode#238
bulgadev wants to merge 89 commits into
patroza:fork/changesfrom
bulgadev:vscode-keybinding-fwdcompat

Conversation

@bulgadev

Copy link
Copy Markdown

Problem

When a T3 Code server introduces new keybinding commands (e.g. filePicker.toggle, projectSearch.toggle), clients built against an older contracts build fail to decode the entire server.getConfig response because ResolvedKeybindingsConfig uses a closed union (KeybindingCommand) that rejects unknown values.

This manifested in the VS Code extension as:

T3 Code pairing failed: Could not connect to T3 Code: Expected "sidebar.toggle" | "terminal.toggle" | ... | "thread.jump.9", got "filePicker.toggle" at ["value"]["keybindings"][42]["command"]

The pairing itself succeeded (bearer token exchanged and stored), but the subsequent getConfig RPC failed during schema validation, blocking the connection.

Solution

Make the decode side of ResolvedKeybindingsConfig forward-compatible: rules whose command does not match the known union are silently dropped on decode, while known rules round-trip unchanged. Encoding is unaffected, so the server (which only ever emits known commands) and the strict authoring path (KeybindingRule / KeybindingsConfig) keep their exact behavior.

This is the same forward-compat pattern used elsewhere in the codebase (e.g. ProviderOptionSelections in model.ts tolerates legacy shapes via Schema.decodeTo + transformOrFail).

Scope

ResolvedKeybindingsConfig is consumed by all four wire schemas that carry resolved keybindings:

  • server.getConfig (initial config fetch)
  • subscribeServerConfig (config stream)
  • serverUpsertKeybinding / serverRemoveKeybinding (mutation results)

All three clients (vscode, web, mobile) share the same RpcClient.make(WsRpcGroup) decode path, so this fix unblocks all of them simultaneously.

Changes

  • packages/contracts/src/keybindings.ts: wrap ResolvedKeybindingsConfig with a decodeTo transform that filters unknown rules via Schema.decodeUnknownOption(ResolvedKeybindingRule) per element.
  • packages/contracts/src/keybindings.test.ts: two new tests verifying unknown commands are dropped and all-unknown arrays decode as empty.

Verification

  • pnpm --filter @t3tools/contracts test: 238 tests passed (19 files)
  • pnpm exec vp check packages/contracts/src/keybindings.ts packages/contracts/src/keybindings.test.ts: 0 errors, 0 warnings
  • ELECTRON_SKIP_BINARY_DOWNLOAD=1 pnpm exec vp run -r --cache --log labeled typecheck: exit 0

tim-smartand others added 30 commits July 30, 2026 18:43
Add custom "Open with" applications
Source: tim-smart#4
Source head: 8c4bdfbc5b57f6b600233244d330f9efa41dc498
Source commits: 08e1a4fb949585c3c441d6d00455fe904f72cd7b,cd43a401c6c148f1fe26cff72104ac527ea189f3,a8370e7502c552ebb064436e42e1c00f86f0946b,8c4bdfbc5b57f6b600233244d330f9efa41dc498
Imported: complete product delta from the source PR.
(cherry picked from commit 9fae005)
Load direnv environments for provider sessions
Source: tim-smart#5
Source head: 8f5fc87c13f4628c179cda44d4f32f7fe4d316b2
Source commits: e4f07014d39964fde2498bcb35588974cc5e6232,0d1463af61e0bd174f698b2519ebf3b207a2eaca,a66e4160d5f4b79140ec8fbcbc6aa66af750a991,8f5fc87c13f4628c179cda44d4f32f7fe4d316b2
Imported: complete product delta from the source PR.
(cherry picked from commit 0da8bfe)
Add unsigned retry for commit signing failures
Source: tim-smart#6
Source head: 7d65c5a224e97a6b811b0a84892f1fda065c5963
Source commits: 18ee567ecfdb11c9372153127b26b5cf57213a76,72a6fae23c86708080c4fed346d5bf0f136f0221,6614b28239ed2330a8f601357a413f2d50da195a,ec169369daa554541511aa28f551b36f3dd26485,7d65c5a224e97a6b811b0a84892f1fda065c5963
Imported: complete product delta from the source PR.
(cherry picked from commit 03671a2)
Add /new command for contextual threads
Source: tim-smart#7
Source head: 2051a8003041fe2806fcb4bc7a0d8940579fc543
Source commits: 2051a8003041fe2806fcb4bc7a0d8940579fc543
Imported: complete product delta from the source PR.
(cherry picked from commit 4d94f31)
Add session dashboard board
Source: tim-smart#8
Source head: d9f8e4d0a8dc22231ca315f3c595c3597f3b13e5
Source commits: 268fb8df9863ffbda51b975a8dbe68f11c41500c,dde20f271f674da22dd8f3a08201c2acf5e58ee5,df4a145e7b2cd2dc17a7a595267d2d8eb0a2a3f0,ce5723ddb0bf630a18d4cb8227b5344d12626e72,ad8c1a6af41161e1fc38a52f681b306517c7b918,6281887e6125317da0c7b4252d59bfd41c9bf35e,550db6316c634febdbe1cb27334d1347c23c7b2a,d9f8e4d0a8dc22231ca315f3c595c3597f3b13e5
Imported: complete product delta from the source PR.
(cherry picked from commit cd0e281)
Recover interrupted provider turns after server restarts
Source: tim-smart#9
Source head: b181832560177250b90bbfe07b0882c9e5b93493
Source commits: 7f69028a25be21f1882ecba14b62f387ad60cf2a,1d52bce1376766d804ef884d7d50b8b6d1b48cf7,b181832560177250b90bbfe07b0882c9e5b93493
Imported: complete product delta from the source PR.
(cherry picked from commit 83de8f5)
Avoid repeated thread snapshot loads during subscription retries
Source: tim-smart#10
Source head: c8c9eadb9de3026706bc3a403ca05b12d0da8dd5
Source commits: c8c9eadb9de3026706bc3a403ca05b12d0da8dd5
Imported: complete product delta from the source PR.
(cherry picked from commit 9e400c3)
Add image upload button to compact chat composer
Source: tim-smart#11
Source head: 1ff63f9b9c418ef56a46c6422d22c01de97581a8
Source commits: 1ff63f9b9c418ef56a46c6422d22c01de97581a8
Imported: complete product delta from the source PR.
(cherry picked from commit 720ec65)
Truncate mobile branch toolbar controls
Source: tim-smart#12
Source head: 1b7d44428472511bc98d8f936654359ce2536901
Source commits: 1b7d44428472511bc98d8f936654359ce2536901
Imported: complete product delta from the source PR.
(cherry picked from commit dc2bbb4)
Clean up worktrees when archiving threads
Source: tim-smart#13
Source head: a23f42d6ac671ea36b8db5d03934c089a31be448
Source commits: 4a194707ed134f993502ac5fdf36a8425f1769cd,1b6688aa5b641010cb2e9dad23d36d87257403ad,9ed32aa3923fb674380564b1ffcb3268290069b9,a23f42d6ac671ea36b8db5d03934c089a31be448
Imported: complete product delta from the source PR.
(cherry picked from commit 7e02dc9)
Pass hosted app channel into Vercel web builds
Source: tim-smart#14
Source head: de6966a6784b4703145c20b84fc482703bca4fa2
Source commits: de6966a6784b4703145c20b84fc482703bca4fa2
Imported: complete product delta from the source PR.
(cherry picked from commit 6333d8d)
Allow worktrees to reuse the selected branch
Source: tim-smart#15
Source head: 2d3900ba36c9397dc4fbe879c613a809f6b45384
Source commits: cd60531253fbafc470f5a5ac18d3e44832d3376d,2d3900ba36c9397dc4fbe879c613a809f6b45384
Imported: complete product delta from the source PR.
(cherry picked from commit 5e7dff2)
Add optional worktree removal confirmation
Source: tim-smart#16
Source head: c3f509fe8f690b704bb34692d9c132c0644db777
Source commits: 76f063e983ca3c39b20f79d8ea83783ab034251a,c3f509fe8f690b704bb34692d9c132c0644db777
Imported: complete product delta from the source PR.
(cherry picked from commit 9886109)
Stop retrying unavailable thread subscriptions
Source: tim-smart#17
Source head: 1359af8ba0b146e3d49f89b72c250f681e86199d
Source commits: 1359af8ba0b146e3d49f89b72c250f681e86199d
Imported: complete product delta from the source PR.
(cherry picked from commit 7b37a7a)
Compatibility fix for running the selected Tim stack on the fork CI matrix. Source adaptation review: patroza#31.
(cherry picked from commit 6edd39a)
Keep the selected Tim Open With feature portable on non-macOS builders and avoid treating custom app definitions as macOS bundles. Source adaptation review: patroza#33.
(cherry picked from commit 15a7d2a)
…nd; green tip
Bring Tim layer tip to typecheck green by joining main ref-refresh VCS client
state with fork failureKind/worktree-cleanup contracts, restoring
filterBrowseEntries/reuse-base-branch surfaces Tim dropped, and fixing
ChatView/Board call-site type errors left by incomplete Tim joins.
(cherry picked from commit 0e24917)
Bring fork/tim typecheck/test green after main pingdotgg#2679 + Tim client-runtime
rewrite: rejoin EnvironmentSubscriptionRpcTag/localApi/ws scopes, wire
BackgroundPolicy/ResourceTelemetry layers, force openpgp for signing tests
on hosts with gpg.format=ssh, and treat TRACE2 child_exit without
child_class as hook finish (git 2.55+).
…troza#29)
Source: pingdotgg#4018
Source SHA: de8fd65
Imported: bounded server activity snapshots, cursor pagination, lazy web history loading, reconnect-safe reset/dedup, and disabled eager browser sidebar hydration.
Adapted: preserved Tim thread lifecycle handling and Omega composer/minimap behavior while resolving current-stack conflicts.
Excluded: none of the source PR behavior; native mobile pagination remains separate because pingdotgg#4018 intentionally excludes it.
…#3510) (patroza#35)
Source: pingdotgg#3510
Source SHA: 034f4936d7a1435887bb62ac3f2db61f08928cbf
Imported: native mobile lazy loading for older thread activity, a 1,000-event subscription catch-up ceiling with snapshot fallback, and synchronized stale snapshot watermarks.
Adapted: applied above the refreshed pingdotgg#4018 web/server candidate and preserved Tim lifecycle handling plus our mobile composer changes.
Excluded: pingdotgg#3510 server/web pagination duplicated by pingdotgg#4018, the later shared-hook refactor, formatting-only commits, and contract comments. The shared refactor can be revisited independently after production validation.
…oza#34)
Source: pingdotgg#4176
Source SHA: 56b6615
Imported: O(1) command read-model maps, deleted-thread eviction, VCS cache cleanup, browser surface cleanup, preview idle TTL, and per-thread UI cleanup.
Adapted: preserved our thread settlement, snooze, and sequential worktree deletion actions while wiring upstream cleanup into the current hook.
Excluded: none.
Co-authored-by: Rusiru Sadathana <27785781+RusiruSadathana@users.noreply.github.com>
patroza#44)
Source: pingdotgg#4506
Source SHA: f7eaa00
Imported unchanged as one candidate provenance commit.
…tgg#4558)
Imported from https://github.com/pingdotgg/t3code/pull/4558\n\nAdapted to retain our provider restart-recovery constants while replacing the local default-title check with the shared policy.
After rebasing candidates onto the green tim tip, restore missing
EnvironmentThread loading fields, ChatView sendDisabledReason/threadSyncPhase
wiring, and orchestration.getThreadActivities auth coverage.
@patroza

Copy link
Copy Markdown
Owner

Thanks a lot for the contribution!

Requesting changes:

  • decodeUnknownOption(ResolvedKeybindingRule) drops every invalid rule, not only rules with unknown commands. Malformed rules using known commands should continue to fail validation; only genuinely unknown command names should be skipped.
  • The maximum-length check runs after filtering. A large input containing unknown or malformed rules can decode to an empty array and bypass the 256-rule limit. Please enforce the limit on the input array before filtering.

@patroza
patrozaforce-pushed the fork/changes branch 23 times, most recently from 2d1f67b to 271c4b2CompareAugust 5, 2026 14:38
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Ports [#237](#237) by
[@bulgadev](https://github.com/bulgadev) onto `fork/dev`.
The original targets the now-frozen `fork/vscode` overlay branch and
lives in a fork this repository
cannot push to, so it could not be rebased in place.
## Attribution
The commit is **authored by `bulgadev <me@bulgaaw.com>`**, not merely
credited — `git cherry-pick`
preserved it and the amend kept it. `Co-authored-by` is added on top as
insurance: `fork/dev` is
squash-only, and a squash can rewrite the author while trailers survive
in the body either way.
Please **merge this rather than #237**, and close#237 pointing here.
## Content
Adds a *T3 Code: Pair with Server…* command accepting a full pairing URL
(`http://host:port/pair#token=…`) or a bare pairing token, exchanges it
for a bearer access token via
the OAuth token-exchange endpoint, and stores it in `SecretStorage` so
`ensureConnected` picks it up
as the bearer tier. Removes the need to hand-exchange tokens with `curl`
before using *Set Server
Bearer Token*.
Unchanged from the original — 5 files, cherry-picked cleanly onto
`fork/dev` with no conflicts.
## Validation
- `apps/vscode` suite: **13 files, 44 tests pass**.
- Full recursive typecheck clean across all 17 packages.
## Note on the other external PR
[#238](#238) is **not** ported,
because it is already fixed on
`fork/dev` by a different route and porting it would duplicate the
behaviour:
- `ResolvedKeybindingsConfig` already uses the generic
`ForwardCompatibleArray` helper, whose decode
keeps only elements that decode and encodes unchanged — identical
semantics to the bespoke
`filterKnownResolvedKeybindingRules` the PR introduces.
- `fork/dev`'s tests are **strictly broader**: unknown commands, unknown
`when`-node types, and
malformed entries. #238 covers only the first.
- One of #238's assertions would now **fail**: it asserts
`filePicker.toggle` decodes away as
unknown, but that command has since shipped and is known.
Recommend closing #238 as already fixed, with credit to @bulgadev for
reporting the class of bug.
Co-authored by [@patroza](https://github.com/patroza),
[@bulgadev](https://github.com/bulgadev)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: bulgadev <me@bulgaaw.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
@omegent-app

Copy link
Copy Markdown

Thanks for this one too, @bulgadev — you were right about the bug, and it is fixed. Closing because
fork/dev already has it, arrived at independently.

ResolvedKeybindingsConfig now goes through a generic ForwardCompatibleArray helper:

exportconstResolvedKeybindingsConfig=ForwardCompatibleArray(ResolvedKeybindingRule).check(Schema.isMaxLength(MAX_KEYBINDINGS_COUNT),);

It decodes each element and keeps only the ones that decode, passing encode through untouched —
the same semantics as your filterKnownResolvedKeybindingRules, generalised so other contracts get
it for free. Merging yours on top would add a second, bespoke implementation of behaviour already in
place.

The test coverage that landed with it is also a superset of the two cases here:

  • rules whose command this build does not know — your case
  • rules with unknown when-node types
  • malformed entries ("garbage", null)

One thing worth flagging, since it says something about how long this sat: your second assertion
would now fail. It expects filePicker.toggle to decode away as unknown, but that command shipped in
the meantime and is in the known set — so a payload containing it decodes to one rule, not zero. The
class of bug you found is exactly why that matters, and the generic fix covers it.

For context on why this went quiet rather than getting reviewed: it targeted fork/changes, which
has since been frozen. This repository moved to a stable, never-rebased fork/dev as the contributor
target — partly because PRs like yours were being silently invalidated by rebases underneath them.
Your other PR (#237) is merged as #351 with your authorship on the commit.

fork/dev is the branch to target from here. Thanks again. 🙏

@omegent-appomegent-appBot closed this Aug 6, 2026
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Records the `fork/dev` development and release model — **adopted and
live since 2026-08-06**. This
started as a proposal; the migration then ran ahead of it, so the
document is now the record rather
than the plan.
Documentation only. Every mechanism it describes is already merged and
running.
## The model
`fork/dev` is the default branch, the contributor target and the release
source. It is never rebased.
The provenance stack `main → fork/base → fork/tim → fork/candidates`
stays rebased and feeds
`fork/dev` through reviewed tree deltas, so contributor bases are never
invalidated by an upstream
update.
| In place | |
| --- | --- |
| `fork/dev` cut from green `fork/integration` `21badd04e`, trees proven
identical | tag `fork-dev/2026-08-06.1` |
| Default branch, ruleset, squash-only, required checks | live |
| CI for `fork/dev` PRs and merges | #343 |
| Deployment promoting exact green `fork/dev` SHAs | ops `deploy.env` |
| Validation and release split | #347, #349 |
| First provenance sync, upstream `2a04db134..a2ca89a` | #345, tag
`fork-dev/2026-08-06.2` |
| Upstream ancestry recorded so "behind" reads true | `3a7e7a458` |
| Overlays drained and deregistered | #348 |
## What this revision corrects
The document had drifted from what was actually built:
- **Release is two workflows, not one.** `fork-ci` decides whether a SHA
is valid; `fork-release`
acts on that verdict via `workflow_run`. A release action must never be
able to veto a validation
verdict — when mobile dispatch lived inside `fork-ci`, one failed EAS
call marked a valid SHA
unapprovable and stranded the whole fleet.
- **Check selection is *not* path-inferred**, and the document
previously implied it should be. Every
PR runs all four required checks; only *release* scope is classified. A
path filter that errs
narrow silently skips a check on a protected branch, which is worse than
a slightly slower suite.
- **`fork/changes` and `fork/integration` are frozen**, not fallbacks.
- Ops parameterization and the `deploy.env` cutover are **done**, not
pending.
- Steps that were "do now" are recorded as done, with real SHAs, tags
and ruleset contents.
## What the cutover surfaced
Added as a section, because each cost a round trip and the old path hid
all of them:
- `fork/dev` had **no CI path at all** — no `push` trigger, not listed
as a `pull_request` base.
- **Mobile releases would have stopped silently**; nothing errors when a
gated job just never fires.
- Both mobile workflows **hardcoded `ref: fork/integration`** and
rejected every `fork/dev` SHA.
- A release failure could **strand the fleet**.
- **Every PR based on `fork/changes` was already broken** by earlier
rebases — GitHub reported them
as 60–100 commits and 629–741 files. Each was one commit of real work on
stale history, fixed by
cherry-picking that commit rather than replaying the branch.
That last one is the clearest evidence for the whole premise: the old
model was silently corrupting
in-flight work, and nobody could see it.
## Deliberately not done
Clean downstream projection is deferred indefinitely and nothing depends
on it. Provenance sync stays
manual. The overlay machinery is still present and still passes its
tests with an empty manifest;
removing it touches ~20 files and is a separate decision.
## Still open
PRs #317, #226 and #185 conflict when cherry-picked onto `fork/dev`;
#237 and #238 live in an
external fork and need their author. `fork/changes` and
`fork/integration` can be deleted once those
are drained.
## Also: no guidance targets an overlay any more
The overlays were drained in #348, but the instructions an agent or
contributor actually reads before
opening a PR still sent them at `fork/discord`, `fork/vscode`,
`fork/identity`, the desktop
deep-links branch, or `fork/changes`. Left alone, the next client-owned
change would have been opened
against a **closed overlay on a frozen branch**.
- **`CLAUDE.md`** (`AGENTS.md` symlinks to it): branch from and target
`fork/dev` for every kind of
work; `main`, `fork/changes` and `fork/integration` named as bases never
to use; the
"register an `integrationOverlays` entry" instructions replaced with a
record that it is empty.
- **`apps/discord-bot/docs/agent-turn-rules.md`**: recovery branches
pointed at *"the correct base
(`fork/discord` overlay / `fork/changes` / etc.)"* → `fork/dev`.
- **`fork-stack.md`, `stack-ship-path.md`, `client-overlays.md`**:
bannered as superseded rather than
rewritten — the provenance stack they document is still current and they
are the record of how the
fork worked before the cutover. The two lines that literally instructed
a base are corrected.
Verified by grep: nothing in the repository still directs a PR anywhere
but `fork/dev`.
## Validation
`vp fmt --check` clean; internal anchors checked. Documentation only —
no code, tooling or workflow
changes in this PR.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@bulgadev@patroza@tim-smart
, '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(contracts): tolerate unknown keybinding commands on client decode - #238

Closed
bulgadev wants to merge 89 commits into
patroza:fork/changesfrom
bulgadev:vscode-keybinding-fwdcompat
Closed

fix(contracts): tolerate unknown keybinding commands on client decode#238
bulgadev wants to merge 89 commits into
patroza:fork/changesfrom
bulgadev:vscode-keybinding-fwdcompat

Conversation

@bulgadev

Copy link
Copy Markdown

Problem

When a T3 Code server introduces new keybinding commands (e.g. filePicker.toggle, projectSearch.toggle), clients built against an older contracts build fail to decode the entire server.getConfig response because ResolvedKeybindingsConfig uses a closed union (KeybindingCommand) that rejects unknown values.

This manifested in the VS Code extension as:

T3 Code pairing failed: Could not connect to T3 Code: Expected "sidebar.toggle" | "terminal.toggle" | ... | "thread.jump.9", got "filePicker.toggle" at ["value"]["keybindings"][42]["command"]

The pairing itself succeeded (bearer token exchanged and stored), but the subsequent getConfig RPC failed during schema validation, blocking the connection.

Solution

Make the decode side of ResolvedKeybindingsConfig forward-compatible: rules whose command does not match the known union are silently dropped on decode, while known rules round-trip unchanged. Encoding is unaffected, so the server (which only ever emits known commands) and the strict authoring path (KeybindingRule / KeybindingsConfig) keep their exact behavior.

This is the same forward-compat pattern used elsewhere in the codebase (e.g. ProviderOptionSelections in model.ts tolerates legacy shapes via Schema.decodeTo + transformOrFail).

Scope

ResolvedKeybindingsConfig is consumed by all four wire schemas that carry resolved keybindings:

  • server.getConfig (initial config fetch)
  • subscribeServerConfig (config stream)
  • serverUpsertKeybinding / serverRemoveKeybinding (mutation results)

All three clients (vscode, web, mobile) share the same RpcClient.make(WsRpcGroup) decode path, so this fix unblocks all of them simultaneously.

Changes

  • packages/contracts/src/keybindings.ts: wrap ResolvedKeybindingsConfig with a decodeTo transform that filters unknown rules via Schema.decodeUnknownOption(ResolvedKeybindingRule) per element.
  • packages/contracts/src/keybindings.test.ts: two new tests verifying unknown commands are dropped and all-unknown arrays decode as empty.

Verification

  • pnpm --filter @t3tools/contracts test: 238 tests passed (19 files)
  • pnpm exec vp check packages/contracts/src/keybindings.ts packages/contracts/src/keybindings.test.ts: 0 errors, 0 warnings
  • ELECTRON_SKIP_BINARY_DOWNLOAD=1 pnpm exec vp run -r --cache --log labeled typecheck: exit 0

tim-smartand others added 30 commits July 30, 2026 18:43
Add custom "Open with" applications
Source: tim-smart#4
Source head: 8c4bdfbc5b57f6b600233244d330f9efa41dc498
Source commits: 08e1a4fb949585c3c441d6d00455fe904f72cd7b,cd43a401c6c148f1fe26cff72104ac527ea189f3,a8370e7502c552ebb064436e42e1c00f86f0946b,8c4bdfbc5b57f6b600233244d330f9efa41dc498
Imported: complete product delta from the source PR.
(cherry picked from commit 9fae005)
Load direnv environments for provider sessions
Source: tim-smart#5
Source head: 8f5fc87c13f4628c179cda44d4f32f7fe4d316b2
Source commits: e4f07014d39964fde2498bcb35588974cc5e6232,0d1463af61e0bd174f698b2519ebf3b207a2eaca,a66e4160d5f4b79140ec8fbcbc6aa66af750a991,8f5fc87c13f4628c179cda44d4f32f7fe4d316b2
Imported: complete product delta from the source PR.
(cherry picked from commit 0da8bfe)
Add unsigned retry for commit signing failures
Source: tim-smart#6
Source head: 7d65c5a224e97a6b811b0a84892f1fda065c5963
Source commits: 18ee567ecfdb11c9372153127b26b5cf57213a76,72a6fae23c86708080c4fed346d5bf0f136f0221,6614b28239ed2330a8f601357a413f2d50da195a,ec169369daa554541511aa28f551b36f3dd26485,7d65c5a224e97a6b811b0a84892f1fda065c5963
Imported: complete product delta from the source PR.
(cherry picked from commit 03671a2)
Add /new command for contextual threads
Source: tim-smart#7
Source head: 2051a8003041fe2806fcb4bc7a0d8940579fc543
Source commits: 2051a8003041fe2806fcb4bc7a0d8940579fc543
Imported: complete product delta from the source PR.
(cherry picked from commit 4d94f31)
Add session dashboard board
Source: tim-smart#8
Source head: d9f8e4d0a8dc22231ca315f3c595c3597f3b13e5
Source commits: 268fb8df9863ffbda51b975a8dbe68f11c41500c,dde20f271f674da22dd8f3a08201c2acf5e58ee5,df4a145e7b2cd2dc17a7a595267d2d8eb0a2a3f0,ce5723ddb0bf630a18d4cb8227b5344d12626e72,ad8c1a6af41161e1fc38a52f681b306517c7b918,6281887e6125317da0c7b4252d59bfd41c9bf35e,550db6316c634febdbe1cb27334d1347c23c7b2a,d9f8e4d0a8dc22231ca315f3c595c3597f3b13e5
Imported: complete product delta from the source PR.
(cherry picked from commit cd0e281)
Recover interrupted provider turns after server restarts
Source: tim-smart#9
Source head: b181832560177250b90bbfe07b0882c9e5b93493
Source commits: 7f69028a25be21f1882ecba14b62f387ad60cf2a,1d52bce1376766d804ef884d7d50b8b6d1b48cf7,b181832560177250b90bbfe07b0882c9e5b93493
Imported: complete product delta from the source PR.
(cherry picked from commit 83de8f5)
Avoid repeated thread snapshot loads during subscription retries
Source: tim-smart#10
Source head: c8c9eadb9de3026706bc3a403ca05b12d0da8dd5
Source commits: c8c9eadb9de3026706bc3a403ca05b12d0da8dd5
Imported: complete product delta from the source PR.
(cherry picked from commit 9e400c3)
Add image upload button to compact chat composer
Source: tim-smart#11
Source head: 1ff63f9b9c418ef56a46c6422d22c01de97581a8
Source commits: 1ff63f9b9c418ef56a46c6422d22c01de97581a8
Imported: complete product delta from the source PR.
(cherry picked from commit 720ec65)
Truncate mobile branch toolbar controls
Source: tim-smart#12
Source head: 1b7d44428472511bc98d8f936654359ce2536901
Source commits: 1b7d44428472511bc98d8f936654359ce2536901
Imported: complete product delta from the source PR.
(cherry picked from commit dc2bbb4)
Clean up worktrees when archiving threads
Source: tim-smart#13
Source head: a23f42d6ac671ea36b8db5d03934c089a31be448
Source commits: 4a194707ed134f993502ac5fdf36a8425f1769cd,1b6688aa5b641010cb2e9dad23d36d87257403ad,9ed32aa3923fb674380564b1ffcb3268290069b9,a23f42d6ac671ea36b8db5d03934c089a31be448
Imported: complete product delta from the source PR.
(cherry picked from commit 7e02dc9)
Pass hosted app channel into Vercel web builds
Source: tim-smart#14
Source head: de6966a6784b4703145c20b84fc482703bca4fa2
Source commits: de6966a6784b4703145c20b84fc482703bca4fa2
Imported: complete product delta from the source PR.
(cherry picked from commit 6333d8d)
Allow worktrees to reuse the selected branch
Source: tim-smart#15
Source head: 2d3900ba36c9397dc4fbe879c613a809f6b45384
Source commits: cd60531253fbafc470f5a5ac18d3e44832d3376d,2d3900ba36c9397dc4fbe879c613a809f6b45384
Imported: complete product delta from the source PR.
(cherry picked from commit 5e7dff2)
Add optional worktree removal confirmation
Source: tim-smart#16
Source head: c3f509fe8f690b704bb34692d9c132c0644db777
Source commits: 76f063e983ca3c39b20f79d8ea83783ab034251a,c3f509fe8f690b704bb34692d9c132c0644db777
Imported: complete product delta from the source PR.
(cherry picked from commit 9886109)
Stop retrying unavailable thread subscriptions
Source: tim-smart#17
Source head: 1359af8ba0b146e3d49f89b72c250f681e86199d
Source commits: 1359af8ba0b146e3d49f89b72c250f681e86199d
Imported: complete product delta from the source PR.
(cherry picked from commit 7b37a7a)
Compatibility fix for running the selected Tim stack on the fork CI matrix. Source adaptation review: patroza#31.
(cherry picked from commit 6edd39a)
Keep the selected Tim Open With feature portable on non-macOS builders and avoid treating custom app definitions as macOS bundles. Source adaptation review: patroza#33.
(cherry picked from commit 15a7d2a)
…nd; green tip
Bring Tim layer tip to typecheck green by joining main ref-refresh VCS client
state with fork failureKind/worktree-cleanup contracts, restoring
filterBrowseEntries/reuse-base-branch surfaces Tim dropped, and fixing
ChatView/Board call-site type errors left by incomplete Tim joins.
(cherry picked from commit 0e24917)
Bring fork/tim typecheck/test green after main pingdotgg#2679 + Tim client-runtime
rewrite: rejoin EnvironmentSubscriptionRpcTag/localApi/ws scopes, wire
BackgroundPolicy/ResourceTelemetry layers, force openpgp for signing tests
on hosts with gpg.format=ssh, and treat TRACE2 child_exit without
child_class as hook finish (git 2.55+).
…troza#29)
Source: pingdotgg#4018
Source SHA: de8fd65
Imported: bounded server activity snapshots, cursor pagination, lazy web history loading, reconnect-safe reset/dedup, and disabled eager browser sidebar hydration.
Adapted: preserved Tim thread lifecycle handling and Omega composer/minimap behavior while resolving current-stack conflicts.
Excluded: none of the source PR behavior; native mobile pagination remains separate because pingdotgg#4018 intentionally excludes it.
…#3510) (patroza#35)
Source: pingdotgg#3510
Source SHA: 034f4936d7a1435887bb62ac3f2db61f08928cbf
Imported: native mobile lazy loading for older thread activity, a 1,000-event subscription catch-up ceiling with snapshot fallback, and synchronized stale snapshot watermarks.
Adapted: applied above the refreshed pingdotgg#4018 web/server candidate and preserved Tim lifecycle handling plus our mobile composer changes.
Excluded: pingdotgg#3510 server/web pagination duplicated by pingdotgg#4018, the later shared-hook refactor, formatting-only commits, and contract comments. The shared refactor can be revisited independently after production validation.
…oza#34)
Source: pingdotgg#4176
Source SHA: 56b6615
Imported: O(1) command read-model maps, deleted-thread eviction, VCS cache cleanup, browser surface cleanup, preview idle TTL, and per-thread UI cleanup.
Adapted: preserved our thread settlement, snooze, and sequential worktree deletion actions while wiring upstream cleanup into the current hook.
Excluded: none.
Co-authored-by: Rusiru Sadathana <27785781+RusiruSadathana@users.noreply.github.com>
patroza#44)
Source: pingdotgg#4506
Source SHA: f7eaa00
Imported unchanged as one candidate provenance commit.
…tgg#4558)
Imported from https://github.com/pingdotgg/t3code/pull/4558\n\nAdapted to retain our provider restart-recovery constants while replacing the local default-title check with the shared policy.
After rebasing candidates onto the green tim tip, restore missing
EnvironmentThread loading fields, ChatView sendDisabledReason/threadSyncPhase
wiring, and orchestration.getThreadActivities auth coverage.
@patroza

Copy link
Copy Markdown
Owner

Thanks a lot for the contribution!

Requesting changes:

  • decodeUnknownOption(ResolvedKeybindingRule) drops every invalid rule, not only rules with unknown commands. Malformed rules using known commands should continue to fail validation; only genuinely unknown command names should be skipped.
  • The maximum-length check runs after filtering. A large input containing unknown or malformed rules can decode to an empty array and bypass the 256-rule limit. Please enforce the limit on the input array before filtering.

@patroza
patrozaforce-pushed the fork/changes branch 23 times, most recently from 2d1f67b to 271c4b2CompareAugust 5, 2026 14:38
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Ports [#237](#237) by
[@bulgadev](https://github.com/bulgadev) onto `fork/dev`.
The original targets the now-frozen `fork/vscode` overlay branch and
lives in a fork this repository
cannot push to, so it could not be rebased in place.
## Attribution
The commit is **authored by `bulgadev <me@bulgaaw.com>`**, not merely
credited — `git cherry-pick`
preserved it and the amend kept it. `Co-authored-by` is added on top as
insurance: `fork/dev` is
squash-only, and a squash can rewrite the author while trailers survive
in the body either way.
Please **merge this rather than #237**, and close#237 pointing here.
## Content
Adds a *T3 Code: Pair with Server…* command accepting a full pairing URL
(`http://host:port/pair#token=…`) or a bare pairing token, exchanges it
for a bearer access token via
the OAuth token-exchange endpoint, and stores it in `SecretStorage` so
`ensureConnected` picks it up
as the bearer tier. Removes the need to hand-exchange tokens with `curl`
before using *Set Server
Bearer Token*.
Unchanged from the original — 5 files, cherry-picked cleanly onto
`fork/dev` with no conflicts.
## Validation
- `apps/vscode` suite: **13 files, 44 tests pass**.
- Full recursive typecheck clean across all 17 packages.
## Note on the other external PR
[#238](#238) is **not** ported,
because it is already fixed on
`fork/dev` by a different route and porting it would duplicate the
behaviour:
- `ResolvedKeybindingsConfig` already uses the generic
`ForwardCompatibleArray` helper, whose decode
keeps only elements that decode and encodes unchanged — identical
semantics to the bespoke
`filterKnownResolvedKeybindingRules` the PR introduces.
- `fork/dev`'s tests are **strictly broader**: unknown commands, unknown
`when`-node types, and
malformed entries. #238 covers only the first.
- One of #238's assertions would now **fail**: it asserts
`filePicker.toggle` decodes away as
unknown, but that command has since shipped and is known.
Recommend closing #238 as already fixed, with credit to @bulgadev for
reporting the class of bug.
Co-authored by [@patroza](https://github.com/patroza),
[@bulgadev](https://github.com/bulgadev)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: bulgadev <me@bulgaaw.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
@omegent-app

Copy link
Copy Markdown

Thanks for this one too, @bulgadev — you were right about the bug, and it is fixed. Closing because
fork/dev already has it, arrived at independently.

ResolvedKeybindingsConfig now goes through a generic ForwardCompatibleArray helper:

exportconstResolvedKeybindingsConfig=ForwardCompatibleArray(ResolvedKeybindingRule).check(Schema.isMaxLength(MAX_KEYBINDINGS_COUNT),);

It decodes each element and keeps only the ones that decode, passing encode through untouched —
the same semantics as your filterKnownResolvedKeybindingRules, generalised so other contracts get
it for free. Merging yours on top would add a second, bespoke implementation of behaviour already in
place.

The test coverage that landed with it is also a superset of the two cases here:

  • rules whose command this build does not know — your case
  • rules with unknown when-node types
  • malformed entries ("garbage", null)

One thing worth flagging, since it says something about how long this sat: your second assertion
would now fail. It expects filePicker.toggle to decode away as unknown, but that command shipped in
the meantime and is in the known set — so a payload containing it decodes to one rule, not zero. The
class of bug you found is exactly why that matters, and the generic fix covers it.

For context on why this went quiet rather than getting reviewed: it targeted fork/changes, which
has since been frozen. This repository moved to a stable, never-rebased fork/dev as the contributor
target — partly because PRs like yours were being silently invalidated by rebases underneath them.
Your other PR (#237) is merged as #351 with your authorship on the commit.

fork/dev is the branch to target from here. Thanks again. 🙏

@omegent-appomegent-appBot closed this Aug 6, 2026
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Records the `fork/dev` development and release model — **adopted and
live since 2026-08-06**. This
started as a proposal; the migration then ran ahead of it, so the
document is now the record rather
than the plan.
Documentation only. Every mechanism it describes is already merged and
running.
## The model
`fork/dev` is the default branch, the contributor target and the release
source. It is never rebased.
The provenance stack `main → fork/base → fork/tim → fork/candidates`
stays rebased and feeds
`fork/dev` through reviewed tree deltas, so contributor bases are never
invalidated by an upstream
update.
| In place | |
| --- | --- |
| `fork/dev` cut from green `fork/integration` `21badd04e`, trees proven
identical | tag `fork-dev/2026-08-06.1` |
| Default branch, ruleset, squash-only, required checks | live |
| CI for `fork/dev` PRs and merges | #343 |
| Deployment promoting exact green `fork/dev` SHAs | ops `deploy.env` |
| Validation and release split | #347, #349 |
| First provenance sync, upstream `2a04db134..a2ca89a` | #345, tag
`fork-dev/2026-08-06.2` |
| Upstream ancestry recorded so "behind" reads true | `3a7e7a458` |
| Overlays drained and deregistered | #348 |
## What this revision corrects
The document had drifted from what was actually built:
- **Release is two workflows, not one.** `fork-ci` decides whether a SHA
is valid; `fork-release`
acts on that verdict via `workflow_run`. A release action must never be
able to veto a validation
verdict — when mobile dispatch lived inside `fork-ci`, one failed EAS
call marked a valid SHA
unapprovable and stranded the whole fleet.
- **Check selection is *not* path-inferred**, and the document
previously implied it should be. Every
PR runs all four required checks; only *release* scope is classified. A
path filter that errs
narrow silently skips a check on a protected branch, which is worse than
a slightly slower suite.
- **`fork/changes` and `fork/integration` are frozen**, not fallbacks.
- Ops parameterization and the `deploy.env` cutover are **done**, not
pending.
- Steps that were "do now" are recorded as done, with real SHAs, tags
and ruleset contents.
## What the cutover surfaced
Added as a section, because each cost a round trip and the old path hid
all of them:
- `fork/dev` had **no CI path at all** — no `push` trigger, not listed
as a `pull_request` base.
- **Mobile releases would have stopped silently**; nothing errors when a
gated job just never fires.
- Both mobile workflows **hardcoded `ref: fork/integration`** and
rejected every `fork/dev` SHA.
- A release failure could **strand the fleet**.
- **Every PR based on `fork/changes` was already broken** by earlier
rebases — GitHub reported them
as 60–100 commits and 629–741 files. Each was one commit of real work on
stale history, fixed by
cherry-picking that commit rather than replaying the branch.
That last one is the clearest evidence for the whole premise: the old
model was silently corrupting
in-flight work, and nobody could see it.
## Deliberately not done
Clean downstream projection is deferred indefinitely and nothing depends
on it. Provenance sync stays
manual. The overlay machinery is still present and still passes its
tests with an empty manifest;
removing it touches ~20 files and is a separate decision.
## Still open
PRs #317, #226 and #185 conflict when cherry-picked onto `fork/dev`;
#237 and #238 live in an
external fork and need their author. `fork/changes` and
`fork/integration` can be deleted once those
are drained.
## Also: no guidance targets an overlay any more
The overlays were drained in #348, but the instructions an agent or
contributor actually reads before
opening a PR still sent them at `fork/discord`, `fork/vscode`,
`fork/identity`, the desktop
deep-links branch, or `fork/changes`. Left alone, the next client-owned
change would have been opened
against a **closed overlay on a frozen branch**.
- **`CLAUDE.md`** (`AGENTS.md` symlinks to it): branch from and target
`fork/dev` for every kind of
work; `main`, `fork/changes` and `fork/integration` named as bases never
to use; the
"register an `integrationOverlays` entry" instructions replaced with a
record that it is empty.
- **`apps/discord-bot/docs/agent-turn-rules.md`**: recovery branches
pointed at *"the correct base
(`fork/discord` overlay / `fork/changes` / etc.)"* → `fork/dev`.
- **`fork-stack.md`, `stack-ship-path.md`, `client-overlays.md`**:
bannered as superseded rather than
rewritten — the provenance stack they document is still current and they
are the record of how the
fork worked before the cutover. The two lines that literally instructed
a base are corrected.
Verified by grep: nothing in the repository still directs a PR anywhere
but `fork/dev`.
## Validation
`vp fmt --check` clean; internal anchors checked. Documentation only —
no code, tooling or workflow
changes in this PR.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@bulgadev@patroza@tim-smart
, '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(contracts): tolerate unknown keybinding commands on client decode - #238

Closed
bulgadev wants to merge 89 commits into
patroza:fork/changesfrom
bulgadev:vscode-keybinding-fwdcompat
Closed

fix(contracts): tolerate unknown keybinding commands on client decode#238
bulgadev wants to merge 89 commits into
patroza:fork/changesfrom
bulgadev:vscode-keybinding-fwdcompat

Conversation

@bulgadev

Copy link
Copy Markdown

Problem

When a T3 Code server introduces new keybinding commands (e.g. filePicker.toggle, projectSearch.toggle), clients built against an older contracts build fail to decode the entire server.getConfig response because ResolvedKeybindingsConfig uses a closed union (KeybindingCommand) that rejects unknown values.

This manifested in the VS Code extension as:

T3 Code pairing failed: Could not connect to T3 Code: Expected "sidebar.toggle" | "terminal.toggle" | ... | "thread.jump.9", got "filePicker.toggle" at ["value"]["keybindings"][42]["command"]

The pairing itself succeeded (bearer token exchanged and stored), but the subsequent getConfig RPC failed during schema validation, blocking the connection.

Solution

Make the decode side of ResolvedKeybindingsConfig forward-compatible: rules whose command does not match the known union are silently dropped on decode, while known rules round-trip unchanged. Encoding is unaffected, so the server (which only ever emits known commands) and the strict authoring path (KeybindingRule / KeybindingsConfig) keep their exact behavior.

This is the same forward-compat pattern used elsewhere in the codebase (e.g. ProviderOptionSelections in model.ts tolerates legacy shapes via Schema.decodeTo + transformOrFail).

Scope

ResolvedKeybindingsConfig is consumed by all four wire schemas that carry resolved keybindings:

  • server.getConfig (initial config fetch)
  • subscribeServerConfig (config stream)
  • serverUpsertKeybinding / serverRemoveKeybinding (mutation results)

All three clients (vscode, web, mobile) share the same RpcClient.make(WsRpcGroup) decode path, so this fix unblocks all of them simultaneously.

Changes

  • packages/contracts/src/keybindings.ts: wrap ResolvedKeybindingsConfig with a decodeTo transform that filters unknown rules via Schema.decodeUnknownOption(ResolvedKeybindingRule) per element.
  • packages/contracts/src/keybindings.test.ts: two new tests verifying unknown commands are dropped and all-unknown arrays decode as empty.

Verification

  • pnpm --filter @t3tools/contracts test: 238 tests passed (19 files)
  • pnpm exec vp check packages/contracts/src/keybindings.ts packages/contracts/src/keybindings.test.ts: 0 errors, 0 warnings
  • ELECTRON_SKIP_BINARY_DOWNLOAD=1 pnpm exec vp run -r --cache --log labeled typecheck: exit 0

tim-smartand others added 30 commits July 30, 2026 18:43
Add custom "Open with" applications
Source: tim-smart#4
Source head: 8c4bdfbc5b57f6b600233244d330f9efa41dc498
Source commits: 08e1a4fb949585c3c441d6d00455fe904f72cd7b,cd43a401c6c148f1fe26cff72104ac527ea189f3,a8370e7502c552ebb064436e42e1c00f86f0946b,8c4bdfbc5b57f6b600233244d330f9efa41dc498
Imported: complete product delta from the source PR.
(cherry picked from commit 9fae005)
Load direnv environments for provider sessions
Source: tim-smart#5
Source head: 8f5fc87c13f4628c179cda44d4f32f7fe4d316b2
Source commits: e4f07014d39964fde2498bcb35588974cc5e6232,0d1463af61e0bd174f698b2519ebf3b207a2eaca,a66e4160d5f4b79140ec8fbcbc6aa66af750a991,8f5fc87c13f4628c179cda44d4f32f7fe4d316b2
Imported: complete product delta from the source PR.
(cherry picked from commit 0da8bfe)
Add unsigned retry for commit signing failures
Source: tim-smart#6
Source head: 7d65c5a224e97a6b811b0a84892f1fda065c5963
Source commits: 18ee567ecfdb11c9372153127b26b5cf57213a76,72a6fae23c86708080c4fed346d5bf0f136f0221,6614b28239ed2330a8f601357a413f2d50da195a,ec169369daa554541511aa28f551b36f3dd26485,7d65c5a224e97a6b811b0a84892f1fda065c5963
Imported: complete product delta from the source PR.
(cherry picked from commit 03671a2)
Add /new command for contextual threads
Source: tim-smart#7
Source head: 2051a8003041fe2806fcb4bc7a0d8940579fc543
Source commits: 2051a8003041fe2806fcb4bc7a0d8940579fc543
Imported: complete product delta from the source PR.
(cherry picked from commit 4d94f31)
Add session dashboard board
Source: tim-smart#8
Source head: d9f8e4d0a8dc22231ca315f3c595c3597f3b13e5
Source commits: 268fb8df9863ffbda51b975a8dbe68f11c41500c,dde20f271f674da22dd8f3a08201c2acf5e58ee5,df4a145e7b2cd2dc17a7a595267d2d8eb0a2a3f0,ce5723ddb0bf630a18d4cb8227b5344d12626e72,ad8c1a6af41161e1fc38a52f681b306517c7b918,6281887e6125317da0c7b4252d59bfd41c9bf35e,550db6316c634febdbe1cb27334d1347c23c7b2a,d9f8e4d0a8dc22231ca315f3c595c3597f3b13e5
Imported: complete product delta from the source PR.
(cherry picked from commit cd0e281)
Recover interrupted provider turns after server restarts
Source: tim-smart#9
Source head: b181832560177250b90bbfe07b0882c9e5b93493
Source commits: 7f69028a25be21f1882ecba14b62f387ad60cf2a,1d52bce1376766d804ef884d7d50b8b6d1b48cf7,b181832560177250b90bbfe07b0882c9e5b93493
Imported: complete product delta from the source PR.
(cherry picked from commit 83de8f5)
Avoid repeated thread snapshot loads during subscription retries
Source: tim-smart#10
Source head: c8c9eadb9de3026706bc3a403ca05b12d0da8dd5
Source commits: c8c9eadb9de3026706bc3a403ca05b12d0da8dd5
Imported: complete product delta from the source PR.
(cherry picked from commit 9e400c3)
Add image upload button to compact chat composer
Source: tim-smart#11
Source head: 1ff63f9b9c418ef56a46c6422d22c01de97581a8
Source commits: 1ff63f9b9c418ef56a46c6422d22c01de97581a8
Imported: complete product delta from the source PR.
(cherry picked from commit 720ec65)
Truncate mobile branch toolbar controls
Source: tim-smart#12
Source head: 1b7d44428472511bc98d8f936654359ce2536901
Source commits: 1b7d44428472511bc98d8f936654359ce2536901
Imported: complete product delta from the source PR.
(cherry picked from commit dc2bbb4)
Clean up worktrees when archiving threads
Source: tim-smart#13
Source head: a23f42d6ac671ea36b8db5d03934c089a31be448
Source commits: 4a194707ed134f993502ac5fdf36a8425f1769cd,1b6688aa5b641010cb2e9dad23d36d87257403ad,9ed32aa3923fb674380564b1ffcb3268290069b9,a23f42d6ac671ea36b8db5d03934c089a31be448
Imported: complete product delta from the source PR.
(cherry picked from commit 7e02dc9)
Pass hosted app channel into Vercel web builds
Source: tim-smart#14
Source head: de6966a6784b4703145c20b84fc482703bca4fa2
Source commits: de6966a6784b4703145c20b84fc482703bca4fa2
Imported: complete product delta from the source PR.
(cherry picked from commit 6333d8d)
Allow worktrees to reuse the selected branch
Source: tim-smart#15
Source head: 2d3900ba36c9397dc4fbe879c613a809f6b45384
Source commits: cd60531253fbafc470f5a5ac18d3e44832d3376d,2d3900ba36c9397dc4fbe879c613a809f6b45384
Imported: complete product delta from the source PR.
(cherry picked from commit 5e7dff2)
Add optional worktree removal confirmation
Source: tim-smart#16
Source head: c3f509fe8f690b704bb34692d9c132c0644db777
Source commits: 76f063e983ca3c39b20f79d8ea83783ab034251a,c3f509fe8f690b704bb34692d9c132c0644db777
Imported: complete product delta from the source PR.
(cherry picked from commit 9886109)
Stop retrying unavailable thread subscriptions
Source: tim-smart#17
Source head: 1359af8ba0b146e3d49f89b72c250f681e86199d
Source commits: 1359af8ba0b146e3d49f89b72c250f681e86199d
Imported: complete product delta from the source PR.
(cherry picked from commit 7b37a7a)
Compatibility fix for running the selected Tim stack on the fork CI matrix. Source adaptation review: patroza#31.
(cherry picked from commit 6edd39a)
Keep the selected Tim Open With feature portable on non-macOS builders and avoid treating custom app definitions as macOS bundles. Source adaptation review: patroza#33.
(cherry picked from commit 15a7d2a)
…nd; green tip
Bring Tim layer tip to typecheck green by joining main ref-refresh VCS client
state with fork failureKind/worktree-cleanup contracts, restoring
filterBrowseEntries/reuse-base-branch surfaces Tim dropped, and fixing
ChatView/Board call-site type errors left by incomplete Tim joins.
(cherry picked from commit 0e24917)
Bring fork/tim typecheck/test green after main pingdotgg#2679 + Tim client-runtime
rewrite: rejoin EnvironmentSubscriptionRpcTag/localApi/ws scopes, wire
BackgroundPolicy/ResourceTelemetry layers, force openpgp for signing tests
on hosts with gpg.format=ssh, and treat TRACE2 child_exit without
child_class as hook finish (git 2.55+).
…troza#29)
Source: pingdotgg#4018
Source SHA: de8fd65
Imported: bounded server activity snapshots, cursor pagination, lazy web history loading, reconnect-safe reset/dedup, and disabled eager browser sidebar hydration.
Adapted: preserved Tim thread lifecycle handling and Omega composer/minimap behavior while resolving current-stack conflicts.
Excluded: none of the source PR behavior; native mobile pagination remains separate because pingdotgg#4018 intentionally excludes it.
…#3510) (patroza#35)
Source: pingdotgg#3510
Source SHA: 034f4936d7a1435887bb62ac3f2db61f08928cbf
Imported: native mobile lazy loading for older thread activity, a 1,000-event subscription catch-up ceiling with snapshot fallback, and synchronized stale snapshot watermarks.
Adapted: applied above the refreshed pingdotgg#4018 web/server candidate and preserved Tim lifecycle handling plus our mobile composer changes.
Excluded: pingdotgg#3510 server/web pagination duplicated by pingdotgg#4018, the later shared-hook refactor, formatting-only commits, and contract comments. The shared refactor can be revisited independently after production validation.
…oza#34)
Source: pingdotgg#4176
Source SHA: 56b6615
Imported: O(1) command read-model maps, deleted-thread eviction, VCS cache cleanup, browser surface cleanup, preview idle TTL, and per-thread UI cleanup.
Adapted: preserved our thread settlement, snooze, and sequential worktree deletion actions while wiring upstream cleanup into the current hook.
Excluded: none.
Co-authored-by: Rusiru Sadathana <27785781+RusiruSadathana@users.noreply.github.com>
patroza#44)
Source: pingdotgg#4506
Source SHA: f7eaa00
Imported unchanged as one candidate provenance commit.
…tgg#4558)
Imported from https://github.com/pingdotgg/t3code/pull/4558\n\nAdapted to retain our provider restart-recovery constants while replacing the local default-title check with the shared policy.
After rebasing candidates onto the green tim tip, restore missing
EnvironmentThread loading fields, ChatView sendDisabledReason/threadSyncPhase
wiring, and orchestration.getThreadActivities auth coverage.
@patroza

Copy link
Copy Markdown
Owner

Thanks a lot for the contribution!

Requesting changes:

  • decodeUnknownOption(ResolvedKeybindingRule) drops every invalid rule, not only rules with unknown commands. Malformed rules using known commands should continue to fail validation; only genuinely unknown command names should be skipped.
  • The maximum-length check runs after filtering. A large input containing unknown or malformed rules can decode to an empty array and bypass the 256-rule limit. Please enforce the limit on the input array before filtering.

@patroza
patrozaforce-pushed the fork/changes branch 23 times, most recently from 2d1f67b to 271c4b2CompareAugust 5, 2026 14:38
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Ports [#237](#237) by
[@bulgadev](https://github.com/bulgadev) onto `fork/dev`.
The original targets the now-frozen `fork/vscode` overlay branch and
lives in a fork this repository
cannot push to, so it could not be rebased in place.
## Attribution
The commit is **authored by `bulgadev <me@bulgaaw.com>`**, not merely
credited — `git cherry-pick`
preserved it and the amend kept it. `Co-authored-by` is added on top as
insurance: `fork/dev` is
squash-only, and a squash can rewrite the author while trailers survive
in the body either way.
Please **merge this rather than #237**, and close#237 pointing here.
## Content
Adds a *T3 Code: Pair with Server…* command accepting a full pairing URL
(`http://host:port/pair#token=…`) or a bare pairing token, exchanges it
for a bearer access token via
the OAuth token-exchange endpoint, and stores it in `SecretStorage` so
`ensureConnected` picks it up
as the bearer tier. Removes the need to hand-exchange tokens with `curl`
before using *Set Server
Bearer Token*.
Unchanged from the original — 5 files, cherry-picked cleanly onto
`fork/dev` with no conflicts.
## Validation
- `apps/vscode` suite: **13 files, 44 tests pass**.
- Full recursive typecheck clean across all 17 packages.
## Note on the other external PR
[#238](#238) is **not** ported,
because it is already fixed on
`fork/dev` by a different route and porting it would duplicate the
behaviour:
- `ResolvedKeybindingsConfig` already uses the generic
`ForwardCompatibleArray` helper, whose decode
keeps only elements that decode and encodes unchanged — identical
semantics to the bespoke
`filterKnownResolvedKeybindingRules` the PR introduces.
- `fork/dev`'s tests are **strictly broader**: unknown commands, unknown
`when`-node types, and
malformed entries. #238 covers only the first.
- One of #238's assertions would now **fail**: it asserts
`filePicker.toggle` decodes away as
unknown, but that command has since shipped and is known.
Recommend closing #238 as already fixed, with credit to @bulgadev for
reporting the class of bug.
Co-authored by [@patroza](https://github.com/patroza),
[@bulgadev](https://github.com/bulgadev)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: bulgadev <me@bulgaaw.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
@omegent-app

Copy link
Copy Markdown

Thanks for this one too, @bulgadev — you were right about the bug, and it is fixed. Closing because
fork/dev already has it, arrived at independently.

ResolvedKeybindingsConfig now goes through a generic ForwardCompatibleArray helper:

exportconstResolvedKeybindingsConfig=ForwardCompatibleArray(ResolvedKeybindingRule).check(Schema.isMaxLength(MAX_KEYBINDINGS_COUNT),);

It decodes each element and keeps only the ones that decode, passing encode through untouched —
the same semantics as your filterKnownResolvedKeybindingRules, generalised so other contracts get
it for free. Merging yours on top would add a second, bespoke implementation of behaviour already in
place.

The test coverage that landed with it is also a superset of the two cases here:

  • rules whose command this build does not know — your case
  • rules with unknown when-node types
  • malformed entries ("garbage", null)

One thing worth flagging, since it says something about how long this sat: your second assertion
would now fail. It expects filePicker.toggle to decode away as unknown, but that command shipped in
the meantime and is in the known set — so a payload containing it decodes to one rule, not zero. The
class of bug you found is exactly why that matters, and the generic fix covers it.

For context on why this went quiet rather than getting reviewed: it targeted fork/changes, which
has since been frozen. This repository moved to a stable, never-rebased fork/dev as the contributor
target — partly because PRs like yours were being silently invalidated by rebases underneath them.
Your other PR (#237) is merged as #351 with your authorship on the commit.

fork/dev is the branch to target from here. Thanks again. 🙏

@omegent-appomegent-appBot closed this Aug 6, 2026
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Records the `fork/dev` development and release model — **adopted and
live since 2026-08-06**. This
started as a proposal; the migration then ran ahead of it, so the
document is now the record rather
than the plan.
Documentation only. Every mechanism it describes is already merged and
running.
## The model
`fork/dev` is the default branch, the contributor target and the release
source. It is never rebased.
The provenance stack `main → fork/base → fork/tim → fork/candidates`
stays rebased and feeds
`fork/dev` through reviewed tree deltas, so contributor bases are never
invalidated by an upstream
update.
| In place | |
| --- | --- |
| `fork/dev` cut from green `fork/integration` `21badd04e`, trees proven
identical | tag `fork-dev/2026-08-06.1` |
| Default branch, ruleset, squash-only, required checks | live |
| CI for `fork/dev` PRs and merges | #343 |
| Deployment promoting exact green `fork/dev` SHAs | ops `deploy.env` |
| Validation and release split | #347, #349 |
| First provenance sync, upstream `2a04db134..a2ca89a` | #345, tag
`fork-dev/2026-08-06.2` |
| Upstream ancestry recorded so "behind" reads true | `3a7e7a458` |
| Overlays drained and deregistered | #348 |
## What this revision corrects
The document had drifted from what was actually built:
- **Release is two workflows, not one.** `fork-ci` decides whether a SHA
is valid; `fork-release`
acts on that verdict via `workflow_run`. A release action must never be
able to veto a validation
verdict — when mobile dispatch lived inside `fork-ci`, one failed EAS
call marked a valid SHA
unapprovable and stranded the whole fleet.
- **Check selection is *not* path-inferred**, and the document
previously implied it should be. Every
PR runs all four required checks; only *release* scope is classified. A
path filter that errs
narrow silently skips a check on a protected branch, which is worse than
a slightly slower suite.
- **`fork/changes` and `fork/integration` are frozen**, not fallbacks.
- Ops parameterization and the `deploy.env` cutover are **done**, not
pending.
- Steps that were "do now" are recorded as done, with real SHAs, tags
and ruleset contents.
## What the cutover surfaced
Added as a section, because each cost a round trip and the old path hid
all of them:
- `fork/dev` had **no CI path at all** — no `push` trigger, not listed
as a `pull_request` base.
- **Mobile releases would have stopped silently**; nothing errors when a
gated job just never fires.
- Both mobile workflows **hardcoded `ref: fork/integration`** and
rejected every `fork/dev` SHA.
- A release failure could **strand the fleet**.
- **Every PR based on `fork/changes` was already broken** by earlier
rebases — GitHub reported them
as 60–100 commits and 629–741 files. Each was one commit of real work on
stale history, fixed by
cherry-picking that commit rather than replaying the branch.
That last one is the clearest evidence for the whole premise: the old
model was silently corrupting
in-flight work, and nobody could see it.
## Deliberately not done
Clean downstream projection is deferred indefinitely and nothing depends
on it. Provenance sync stays
manual. The overlay machinery is still present and still passes its
tests with an empty manifest;
removing it touches ~20 files and is a separate decision.
## Still open
PRs #317, #226 and #185 conflict when cherry-picked onto `fork/dev`;
#237 and #238 live in an
external fork and need their author. `fork/changes` and
`fork/integration` can be deleted once those
are drained.
## Also: no guidance targets an overlay any more
The overlays were drained in #348, but the instructions an agent or
contributor actually reads before
opening a PR still sent them at `fork/discord`, `fork/vscode`,
`fork/identity`, the desktop
deep-links branch, or `fork/changes`. Left alone, the next client-owned
change would have been opened
against a **closed overlay on a frozen branch**.
- **`CLAUDE.md`** (`AGENTS.md` symlinks to it): branch from and target
`fork/dev` for every kind of
work; `main`, `fork/changes` and `fork/integration` named as bases never
to use; the
"register an `integrationOverlays` entry" instructions replaced with a
record that it is empty.
- **`apps/discord-bot/docs/agent-turn-rules.md`**: recovery branches
pointed at *"the correct base
(`fork/discord` overlay / `fork/changes` / etc.)"* → `fork/dev`.
- **`fork-stack.md`, `stack-ship-path.md`, `client-overlays.md`**:
bannered as superseded rather than
rewritten — the provenance stack they document is still current and they
are the record of how the
fork worked before the cutover. The two lines that literally instructed
a base are corrected.
Verified by grep: nothing in the repository still directs a PR anywhere
but `fork/dev`.
## Validation
`vp fmt --check` clean; internal anchors checked. Documentation only —
no code, tooling or workflow
changes in this PR.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@bulgadev@patroza@tim-smart
, '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(contracts): tolerate unknown keybinding commands on client decode - #238

Closed
bulgadev wants to merge 89 commits into
patroza:fork/changesfrom
bulgadev:vscode-keybinding-fwdcompat
Closed

fix(contracts): tolerate unknown keybinding commands on client decode#238
bulgadev wants to merge 89 commits into
patroza:fork/changesfrom
bulgadev:vscode-keybinding-fwdcompat

Conversation

@bulgadev

Copy link
Copy Markdown

Problem

When a T3 Code server introduces new keybinding commands (e.g. filePicker.toggle, projectSearch.toggle), clients built against an older contracts build fail to decode the entire server.getConfig response because ResolvedKeybindingsConfig uses a closed union (KeybindingCommand) that rejects unknown values.

This manifested in the VS Code extension as:

T3 Code pairing failed: Could not connect to T3 Code: Expected "sidebar.toggle" | "terminal.toggle" | ... | "thread.jump.9", got "filePicker.toggle" at ["value"]["keybindings"][42]["command"]

The pairing itself succeeded (bearer token exchanged and stored), but the subsequent getConfig RPC failed during schema validation, blocking the connection.

Solution

Make the decode side of ResolvedKeybindingsConfig forward-compatible: rules whose command does not match the known union are silently dropped on decode, while known rules round-trip unchanged. Encoding is unaffected, so the server (which only ever emits known commands) and the strict authoring path (KeybindingRule / KeybindingsConfig) keep their exact behavior.

This is the same forward-compat pattern used elsewhere in the codebase (e.g. ProviderOptionSelections in model.ts tolerates legacy shapes via Schema.decodeTo + transformOrFail).

Scope

ResolvedKeybindingsConfig is consumed by all four wire schemas that carry resolved keybindings:

  • server.getConfig (initial config fetch)
  • subscribeServerConfig (config stream)
  • serverUpsertKeybinding / serverRemoveKeybinding (mutation results)

All three clients (vscode, web, mobile) share the same RpcClient.make(WsRpcGroup) decode path, so this fix unblocks all of them simultaneously.

Changes

  • packages/contracts/src/keybindings.ts: wrap ResolvedKeybindingsConfig with a decodeTo transform that filters unknown rules via Schema.decodeUnknownOption(ResolvedKeybindingRule) per element.
  • packages/contracts/src/keybindings.test.ts: two new tests verifying unknown commands are dropped and all-unknown arrays decode as empty.

Verification

  • pnpm --filter @t3tools/contracts test: 238 tests passed (19 files)
  • pnpm exec vp check packages/contracts/src/keybindings.ts packages/contracts/src/keybindings.test.ts: 0 errors, 0 warnings
  • ELECTRON_SKIP_BINARY_DOWNLOAD=1 pnpm exec vp run -r --cache --log labeled typecheck: exit 0

tim-smartand others added 30 commits July 30, 2026 18:43
Add custom "Open with" applications
Source: tim-smart#4
Source head: 8c4bdfbc5b57f6b600233244d330f9efa41dc498
Source commits: 08e1a4fb949585c3c441d6d00455fe904f72cd7b,cd43a401c6c148f1fe26cff72104ac527ea189f3,a8370e7502c552ebb064436e42e1c00f86f0946b,8c4bdfbc5b57f6b600233244d330f9efa41dc498
Imported: complete product delta from the source PR.
(cherry picked from commit 9fae005)
Load direnv environments for provider sessions
Source: tim-smart#5
Source head: 8f5fc87c13f4628c179cda44d4f32f7fe4d316b2
Source commits: e4f07014d39964fde2498bcb35588974cc5e6232,0d1463af61e0bd174f698b2519ebf3b207a2eaca,a66e4160d5f4b79140ec8fbcbc6aa66af750a991,8f5fc87c13f4628c179cda44d4f32f7fe4d316b2
Imported: complete product delta from the source PR.
(cherry picked from commit 0da8bfe)
Add unsigned retry for commit signing failures
Source: tim-smart#6
Source head: 7d65c5a224e97a6b811b0a84892f1fda065c5963
Source commits: 18ee567ecfdb11c9372153127b26b5cf57213a76,72a6fae23c86708080c4fed346d5bf0f136f0221,6614b28239ed2330a8f601357a413f2d50da195a,ec169369daa554541511aa28f551b36f3dd26485,7d65c5a224e97a6b811b0a84892f1fda065c5963
Imported: complete product delta from the source PR.
(cherry picked from commit 03671a2)
Add /new command for contextual threads
Source: tim-smart#7
Source head: 2051a8003041fe2806fcb4bc7a0d8940579fc543
Source commits: 2051a8003041fe2806fcb4bc7a0d8940579fc543
Imported: complete product delta from the source PR.
(cherry picked from commit 4d94f31)
Add session dashboard board
Source: tim-smart#8
Source head: d9f8e4d0a8dc22231ca315f3c595c3597f3b13e5
Source commits: 268fb8df9863ffbda51b975a8dbe68f11c41500c,dde20f271f674da22dd8f3a08201c2acf5e58ee5,df4a145e7b2cd2dc17a7a595267d2d8eb0a2a3f0,ce5723ddb0bf630a18d4cb8227b5344d12626e72,ad8c1a6af41161e1fc38a52f681b306517c7b918,6281887e6125317da0c7b4252d59bfd41c9bf35e,550db6316c634febdbe1cb27334d1347c23c7b2a,d9f8e4d0a8dc22231ca315f3c595c3597f3b13e5
Imported: complete product delta from the source PR.
(cherry picked from commit cd0e281)
Recover interrupted provider turns after server restarts
Source: tim-smart#9
Source head: b181832560177250b90bbfe07b0882c9e5b93493
Source commits: 7f69028a25be21f1882ecba14b62f387ad60cf2a,1d52bce1376766d804ef884d7d50b8b6d1b48cf7,b181832560177250b90bbfe07b0882c9e5b93493
Imported: complete product delta from the source PR.
(cherry picked from commit 83de8f5)
Avoid repeated thread snapshot loads during subscription retries
Source: tim-smart#10
Source head: c8c9eadb9de3026706bc3a403ca05b12d0da8dd5
Source commits: c8c9eadb9de3026706bc3a403ca05b12d0da8dd5
Imported: complete product delta from the source PR.
(cherry picked from commit 9e400c3)
Add image upload button to compact chat composer
Source: tim-smart#11
Source head: 1ff63f9b9c418ef56a46c6422d22c01de97581a8
Source commits: 1ff63f9b9c418ef56a46c6422d22c01de97581a8
Imported: complete product delta from the source PR.
(cherry picked from commit 720ec65)
Truncate mobile branch toolbar controls
Source: tim-smart#12
Source head: 1b7d44428472511bc98d8f936654359ce2536901
Source commits: 1b7d44428472511bc98d8f936654359ce2536901
Imported: complete product delta from the source PR.
(cherry picked from commit dc2bbb4)
Clean up worktrees when archiving threads
Source: tim-smart#13
Source head: a23f42d6ac671ea36b8db5d03934c089a31be448
Source commits: 4a194707ed134f993502ac5fdf36a8425f1769cd,1b6688aa5b641010cb2e9dad23d36d87257403ad,9ed32aa3923fb674380564b1ffcb3268290069b9,a23f42d6ac671ea36b8db5d03934c089a31be448
Imported: complete product delta from the source PR.
(cherry picked from commit 7e02dc9)
Pass hosted app channel into Vercel web builds
Source: tim-smart#14
Source head: de6966a6784b4703145c20b84fc482703bca4fa2
Source commits: de6966a6784b4703145c20b84fc482703bca4fa2
Imported: complete product delta from the source PR.
(cherry picked from commit 6333d8d)
Allow worktrees to reuse the selected branch
Source: tim-smart#15
Source head: 2d3900ba36c9397dc4fbe879c613a809f6b45384
Source commits: cd60531253fbafc470f5a5ac18d3e44832d3376d,2d3900ba36c9397dc4fbe879c613a809f6b45384
Imported: complete product delta from the source PR.
(cherry picked from commit 5e7dff2)
Add optional worktree removal confirmation
Source: tim-smart#16
Source head: c3f509fe8f690b704bb34692d9c132c0644db777
Source commits: 76f063e983ca3c39b20f79d8ea83783ab034251a,c3f509fe8f690b704bb34692d9c132c0644db777
Imported: complete product delta from the source PR.
(cherry picked from commit 9886109)
Stop retrying unavailable thread subscriptions
Source: tim-smart#17
Source head: 1359af8ba0b146e3d49f89b72c250f681e86199d
Source commits: 1359af8ba0b146e3d49f89b72c250f681e86199d
Imported: complete product delta from the source PR.
(cherry picked from commit 7b37a7a)
Compatibility fix for running the selected Tim stack on the fork CI matrix. Source adaptation review: patroza#31.
(cherry picked from commit 6edd39a)
Keep the selected Tim Open With feature portable on non-macOS builders and avoid treating custom app definitions as macOS bundles. Source adaptation review: patroza#33.
(cherry picked from commit 15a7d2a)
…nd; green tip
Bring Tim layer tip to typecheck green by joining main ref-refresh VCS client
state with fork failureKind/worktree-cleanup contracts, restoring
filterBrowseEntries/reuse-base-branch surfaces Tim dropped, and fixing
ChatView/Board call-site type errors left by incomplete Tim joins.
(cherry picked from commit 0e24917)
Bring fork/tim typecheck/test green after main pingdotgg#2679 + Tim client-runtime
rewrite: rejoin EnvironmentSubscriptionRpcTag/localApi/ws scopes, wire
BackgroundPolicy/ResourceTelemetry layers, force openpgp for signing tests
on hosts with gpg.format=ssh, and treat TRACE2 child_exit without
child_class as hook finish (git 2.55+).
…troza#29)
Source: pingdotgg#4018
Source SHA: de8fd65
Imported: bounded server activity snapshots, cursor pagination, lazy web history loading, reconnect-safe reset/dedup, and disabled eager browser sidebar hydration.
Adapted: preserved Tim thread lifecycle handling and Omega composer/minimap behavior while resolving current-stack conflicts.
Excluded: none of the source PR behavior; native mobile pagination remains separate because pingdotgg#4018 intentionally excludes it.
…#3510) (patroza#35)
Source: pingdotgg#3510
Source SHA: 034f4936d7a1435887bb62ac3f2db61f08928cbf
Imported: native mobile lazy loading for older thread activity, a 1,000-event subscription catch-up ceiling with snapshot fallback, and synchronized stale snapshot watermarks.
Adapted: applied above the refreshed pingdotgg#4018 web/server candidate and preserved Tim lifecycle handling plus our mobile composer changes.
Excluded: pingdotgg#3510 server/web pagination duplicated by pingdotgg#4018, the later shared-hook refactor, formatting-only commits, and contract comments. The shared refactor can be revisited independently after production validation.
…oza#34)
Source: pingdotgg#4176
Source SHA: 56b6615
Imported: O(1) command read-model maps, deleted-thread eviction, VCS cache cleanup, browser surface cleanup, preview idle TTL, and per-thread UI cleanup.
Adapted: preserved our thread settlement, snooze, and sequential worktree deletion actions while wiring upstream cleanup into the current hook.
Excluded: none.
Co-authored-by: Rusiru Sadathana <27785781+RusiruSadathana@users.noreply.github.com>
patroza#44)
Source: pingdotgg#4506
Source SHA: f7eaa00
Imported unchanged as one candidate provenance commit.
…tgg#4558)
Imported from https://github.com/pingdotgg/t3code/pull/4558\n\nAdapted to retain our provider restart-recovery constants while replacing the local default-title check with the shared policy.
After rebasing candidates onto the green tim tip, restore missing
EnvironmentThread loading fields, ChatView sendDisabledReason/threadSyncPhase
wiring, and orchestration.getThreadActivities auth coverage.
@patroza

Copy link
Copy Markdown
Owner

Thanks a lot for the contribution!

Requesting changes:

  • decodeUnknownOption(ResolvedKeybindingRule) drops every invalid rule, not only rules with unknown commands. Malformed rules using known commands should continue to fail validation; only genuinely unknown command names should be skipped.
  • The maximum-length check runs after filtering. A large input containing unknown or malformed rules can decode to an empty array and bypass the 256-rule limit. Please enforce the limit on the input array before filtering.

@patroza
patrozaforce-pushed the fork/changes branch 23 times, most recently from 2d1f67b to 271c4b2CompareAugust 5, 2026 14:38
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Ports [#237](#237) by
[@bulgadev](https://github.com/bulgadev) onto `fork/dev`.
The original targets the now-frozen `fork/vscode` overlay branch and
lives in a fork this repository
cannot push to, so it could not be rebased in place.
## Attribution
The commit is **authored by `bulgadev <me@bulgaaw.com>`**, not merely
credited — `git cherry-pick`
preserved it and the amend kept it. `Co-authored-by` is added on top as
insurance: `fork/dev` is
squash-only, and a squash can rewrite the author while trailers survive
in the body either way.
Please **merge this rather than #237**, and close#237 pointing here.
## Content
Adds a *T3 Code: Pair with Server…* command accepting a full pairing URL
(`http://host:port/pair#token=…`) or a bare pairing token, exchanges it
for a bearer access token via
the OAuth token-exchange endpoint, and stores it in `SecretStorage` so
`ensureConnected` picks it up
as the bearer tier. Removes the need to hand-exchange tokens with `curl`
before using *Set Server
Bearer Token*.
Unchanged from the original — 5 files, cherry-picked cleanly onto
`fork/dev` with no conflicts.
## Validation
- `apps/vscode` suite: **13 files, 44 tests pass**.
- Full recursive typecheck clean across all 17 packages.
## Note on the other external PR
[#238](#238) is **not** ported,
because it is already fixed on
`fork/dev` by a different route and porting it would duplicate the
behaviour:
- `ResolvedKeybindingsConfig` already uses the generic
`ForwardCompatibleArray` helper, whose decode
keeps only elements that decode and encodes unchanged — identical
semantics to the bespoke
`filterKnownResolvedKeybindingRules` the PR introduces.
- `fork/dev`'s tests are **strictly broader**: unknown commands, unknown
`when`-node types, and
malformed entries. #238 covers only the first.
- One of #238's assertions would now **fail**: it asserts
`filePicker.toggle` decodes away as
unknown, but that command has since shipped and is known.
Recommend closing #238 as already fixed, with credit to @bulgadev for
reporting the class of bug.
Co-authored by [@patroza](https://github.com/patroza),
[@bulgadev](https://github.com/bulgadev)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: bulgadev <me@bulgaaw.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
@omegent-app

Copy link
Copy Markdown

Thanks for this one too, @bulgadev — you were right about the bug, and it is fixed. Closing because
fork/dev already has it, arrived at independently.

ResolvedKeybindingsConfig now goes through a generic ForwardCompatibleArray helper:

exportconstResolvedKeybindingsConfig=ForwardCompatibleArray(ResolvedKeybindingRule).check(Schema.isMaxLength(MAX_KEYBINDINGS_COUNT),);

It decodes each element and keeps only the ones that decode, passing encode through untouched —
the same semantics as your filterKnownResolvedKeybindingRules, generalised so other contracts get
it for free. Merging yours on top would add a second, bespoke implementation of behaviour already in
place.

The test coverage that landed with it is also a superset of the two cases here:

  • rules whose command this build does not know — your case
  • rules with unknown when-node types
  • malformed entries ("garbage", null)

One thing worth flagging, since it says something about how long this sat: your second assertion
would now fail. It expects filePicker.toggle to decode away as unknown, but that command shipped in
the meantime and is in the known set — so a payload containing it decodes to one rule, not zero. The
class of bug you found is exactly why that matters, and the generic fix covers it.

For context on why this went quiet rather than getting reviewed: it targeted fork/changes, which
has since been frozen. This repository moved to a stable, never-rebased fork/dev as the contributor
target — partly because PRs like yours were being silently invalidated by rebases underneath them.
Your other PR (#237) is merged as #351 with your authorship on the commit.

fork/dev is the branch to target from here. Thanks again. 🙏

@omegent-appomegent-appBot closed this Aug 6, 2026
omegent-appBot added a commit that referenced this pull request Aug 6, 2026
Records the `fork/dev` development and release model — **adopted and
live since 2026-08-06**. This
started as a proposal; the migration then ran ahead of it, so the
document is now the record rather
than the plan.
Documentation only. Every mechanism it describes is already merged and
running.
## The model
`fork/dev` is the default branch, the contributor target and the release
source. It is never rebased.
The provenance stack `main → fork/base → fork/tim → fork/candidates`
stays rebased and feeds
`fork/dev` through reviewed tree deltas, so contributor bases are never
invalidated by an upstream
update.
| In place | |
| --- | --- |
| `fork/dev` cut from green `fork/integration` `21badd04e`, trees proven
identical | tag `fork-dev/2026-08-06.1` |
| Default branch, ruleset, squash-only, required checks | live |
| CI for `fork/dev` PRs and merges | #343 |
| Deployment promoting exact green `fork/dev` SHAs | ops `deploy.env` |
| Validation and release split | #347, #349 |
| First provenance sync, upstream `2a04db134..a2ca89a` | #345, tag
`fork-dev/2026-08-06.2` |
| Upstream ancestry recorded so "behind" reads true | `3a7e7a458` |
| Overlays drained and deregistered | #348 |
## What this revision corrects
The document had drifted from what was actually built:
- **Release is two workflows, not one.** `fork-ci` decides whether a SHA
is valid; `fork-release`
acts on that verdict via `workflow_run`. A release action must never be
able to veto a validation
verdict — when mobile dispatch lived inside `fork-ci`, one failed EAS
call marked a valid SHA
unapprovable and stranded the whole fleet.
- **Check selection is *not* path-inferred**, and the document
previously implied it should be. Every
PR runs all four required checks; only *release* scope is classified. A
path filter that errs
narrow silently skips a check on a protected branch, which is worse than
a slightly slower suite.
- **`fork/changes` and `fork/integration` are frozen**, not fallbacks.
- Ops parameterization and the `deploy.env` cutover are **done**, not
pending.
- Steps that were "do now" are recorded as done, with real SHAs, tags
and ruleset contents.
## What the cutover surfaced
Added as a section, because each cost a round trip and the old path hid
all of them:
- `fork/dev` had **no CI path at all** — no `push` trigger, not listed
as a `pull_request` base.
- **Mobile releases would have stopped silently**; nothing errors when a
gated job just never fires.
- Both mobile workflows **hardcoded `ref: fork/integration`** and
rejected every `fork/dev` SHA.
- A release failure could **strand the fleet**.
- **Every PR based on `fork/changes` was already broken** by earlier
rebases — GitHub reported them
as 60–100 commits and 629–741 files. Each was one commit of real work on
stale history, fixed by
cherry-picking that commit rather than replaying the branch.
That last one is the clearest evidence for the whole premise: the old
model was silently corrupting
in-flight work, and nobody could see it.
## Deliberately not done
Clean downstream projection is deferred indefinitely and nothing depends
on it. Provenance sync stays
manual. The overlay machinery is still present and still passes its
tests with an empty manifest;
removing it touches ~20 files and is a separate decision.
## Still open
PRs #317, #226 and #185 conflict when cherry-picked onto `fork/dev`;
#237 and #238 live in an
external fork and need their author. `fork/changes` and
`fork/integration` can be deleted once those
are drained.
## Also: no guidance targets an overlay any more
The overlays were drained in #348, but the instructions an agent or
contributor actually reads before
opening a PR still sent them at `fork/discord`, `fork/vscode`,
`fork/identity`, the desktop
deep-links branch, or `fork/changes`. Left alone, the next client-owned
change would have been opened
against a **closed overlay on a frozen branch**.
- **`CLAUDE.md`** (`AGENTS.md` symlinks to it): branch from and target
`fork/dev` for every kind of
work; `main`, `fork/changes` and `fork/integration` named as bases never
to use; the
"register an `integrationOverlays` entry" instructions replaced with a
record that it is empty.
- **`apps/discord-bot/docs/agent-turn-rules.md`**: recovery branches
pointed at *"the correct base
(`fork/discord` overlay / `fork/changes` / etc.)"* → `fork/dev`.
- **`fork-stack.md`, `stack-ship-path.md`, `client-overlays.md`**:
bannered as superseded rather than
rewritten — the provenance stack they document is still current and they
are the record of how the
fork worked before the cutover. The two lines that literally instructed
a base are corrected.
Verified by grep: nothing in the repository still directs a PR anywhere
but `fork/dev`.
## Validation
`vp fmt --check` clean; internal anchors checked. Documentation only —
no code, tooling or workflow
changes in this PR.
Co-authored by [@patroza](https://github.com/patroza)
opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)
---------
Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@bulgadev@patroza@tim-smart