Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
78e2340
refactor(server): simplify error transformation with Effect.mapError …
aoright Aug 18, 2026
cd814ce
fix(preview): open local environment ports on localhost (#7300)
gbarros-dev Aug 18, 2026
2c37944
fix(desktop): prevent quit shortcut spillover (#7397)
Bil0000 Aug 18, 2026
cc8e122
fix(desktop): stop overwriting a custom dock icon on launch (#7125)
Rishet11 Aug 18, 2026
1151e71
feat(web): show project location in new thread picker (#7392)
StiensWout Aug 18, 2026
2db108e
fix(web): label pull request merge actions (#7381)
tarik02 Aug 18, 2026
fb0c7c9
fix(server): avoid PRs inherited from default upstreams (#7317)
gsimone Aug 18, 2026
0177200
fix(desktop): stop the passkey dialog from popping as soon as sign-in…
t3dotgg Aug 18, 2026
5c63966
feat(desktop): mute a browser tab (#7252)
chrisdeeming Aug 18, 2026
aedcbf4
fix(web): improve disconnected composer placeholder (#7122)
inayayousfi Aug 18, 2026
917ad19
fix(desktop): throttle hidden preview rendering (#7445)
t3-code[bot] Aug 19, 2026
a6bb304
fix(server): stop probing Grok, Cursor, and OpenCode unless turned on…
t3dotgg Aug 19, 2026
76a8722
fix(desktop): boot the main window unthrottled so cold start paints a…
t3dotgg Aug 19, 2026
fc8dae1
fix(threads): a merged PR settles its thread only once (#7454)
t3dotgg Aug 19, 2026
5470cd6
fix(marketing): Safari gets the arm64 Mac download (#7473)
t3dotgg Aug 19, 2026
821e822
feat(web): add shortcuts to the surface dropdown (#7318)
gsimone Aug 19, 2026
0342c46
fix(marketing): never serve the Intel build to Apple Silicon Macs (#7…
t3dotgg Aug 19, 2026
44b1c52
fix(web): animate command palette when closing (#5169)
tarik02 Aug 19, 2026
0e4ab91
fix(desktop): upgrade Clerk OAuth transport (#7479)
t3dotgg Aug 19, 2026
8aae774
feat(server): run the background service on macOS via launchd (#6286)
t3dotgg Aug 19, 2026
7d0a295
fix(server): keep the inherited-upstream PR guard a skipped lookup
rynfar Aug 19, 2026
494e840
docs(upstream): record the 2026-08-19 T3 Code batch
rynfar Aug 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 86 additions & 2 deletions .agents/upstream-review.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
remote: t3code-upstream
branch: main
reviewed-through: "82b8a9380298509d68170961d9717be62836e490"
reviewed-through-date: "2026-08-18"
reviewed-through: "2aa5f095fc3bb65c00cc4efce66a5473e2d4554a"
reviewed-through-date: "2026-08-19"
---

# T3 upstream review log
Expand Down Expand Up @@ -1618,6 +1618,90 @@ rather than on busy pull requests generally.

**Mobile (N7, N8) was not exercised at all.**

## 2026-08-19 — `82b8a9380298509d68170961d9717be62836e490..2aa5f095fc3bb65c00cc4efce66a5473e2d4554a`

Twenty-two upstream commits, ten change sets, one PR. **Twenty commits adopted** onto
`upstream/2026-08-19-batch`; two skipped. `git cherry` reported every one absent from Pylon,
so nothing was patch-equivalent. The deferred register was empty going in and stays empty.

**CS-1 (`#7459`) is the most valuable change here.** It flips the Grok and OpenCode schema
defaults to `false` (Cursor was already `false` in Pylon) and adds
`providerInstanceConfigEnabledFlag` / `resolveProviderInstanceEnabled`, resolving an
envelope-vs-config `enabled` conflict most-restrictively so a user's explicit disable is
never silently undone. **Accepted consequence worth remembering:** `writeSettingsAtomically`
strips values equal to the default, so an install that had Grok on by matching the old
default has nothing written for it and comes back disabled after this change. That is the
point of the commit rather than a regression, but it is a one-way default flip with no
migration, and the sparse-persistence detail means an explicit opt-in is indistinguishable
from an untouched default.

**CS-2 (`#7473` then `#7477`) had to be taken as an ordered pair.** `#7477` deletes
`macArch.ts` and `macArch.test.ts` outright and moves detection into `download.astro` and
`index.astro`, superseding the fix in `#7473`. Adopting only the later commit risks a
delete/modify conflict. The end state deliberately serves **every** Mac the arm64 build from
the hero button, with a code comment saying not to add arch detection back — browsers cannot
tell Apple Silicon from Intel, and Intel users choose on `/download`.

**CS-3 (`#7445` then `#7460`) is likewise inseparable**: the first throttles hidden preview
rendering, the second exempts cold start because the first regressed first paint.

**The launchd change (`#6286`) carried every branding conflict in the batch** — three files,
all resolved Pylon-first by taking upstream's platform-aware behavior and keeping Pylon's
copy. The launch agent label stays the compatibility identifier `com.t3tools.t3code.service`
per AGENTS.md; see the open question below.

Conflicts and adaptations, all resolved Pylon-first:

| File | Conflict | Resolution |
| ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `apps/web/src/components/ThreadCommandSubtitle.tsx` | `#7392` adds a `ThreadCommandSubtitleVariant` design-review toggle; Pylon had simplified that away | Kept Pylon's single `WorkspaceIcon(isWorktree)` and dropped the variant harness; adopted the `COMMAND_PALETTE_META_ICON_CLASS` / `CommandPaletteMetaDot` renames the new palette imports. Nothing outside the file referenced the variant. |
| `apps/web/src/components/chat/ChatComposer.tsx` | `#7122` against a file that has diverged structurally (Pylon 4240 lines vs upstream 2824) | Manual port of the two-line change (import + placeholder constant) onto Pylon's file. Applied byte-wise because the file carries 6 NUL bytes and defeats `grep`. |
| `apps/web/src/providerInstances.ts`, `.../settings/ProviderSettingsPanel.tsx` | Import-list collision: Pylon's `providerInstancePrioritySortKey` vs upstream's `resolveProviderInstanceEnabled` | Union — both symbols are used. |
| `apps/server/src/cli/service.ts`, `apps/server/src/cli/connect.ts` | `#6286` platform-aware copy written as "T3 Code" / "T3 Connect" | Took the darwin/else branching, kept Pylon naming. |
| `docs/user/background-service.md` | Same, plus a whole new Platform Support section | Adopted the macOS content verbatim except product names; kept the literal `t3code.service` and `com.t3tools.t3code.service.plist` paths because that is what the code writes. |

Skipped:

| ID | Upstream | Why |
| ----- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| CS-10 | `db0659fe` (`#7421`) | AUR launcher icon paths. Pylon has no `packaging/aur` directory, so it is not applicable. |
| CS-9 | `324ddda3` (`#6563`) | `npx t3 triage`. 779 insertions whose playbook hardcodes `pingdotgg/t3code`, fetches the playbook from T3's raw GitHub URL, searches T3's issues, and builds a `github.com/pingdotgg/t3code/issues/new` URL. Adopting as-is would funnel Pylon users' bug reports into T3's tracker. Held for a from-scratch Pylon implementation, which needs a product decision first: `pylon-code/pylon` is private, so there is no public tracker for a user's generated issue to land in. |

Review and validation:

- 21 changed test files, **617 tests, 0 failures**. Note for future sessions: `vp test run`
globs nested worktrees under `.prime/` and `.claude/worktrees/`, which have no
`node_modules`, producing ~72 phantom module-resolution failures **and still exiting 0**.
Pass `--exclude '**/.prime/**' --exclude '**/.claude/worktrees/**'` for a real signal.
- Typecheck clean across 8 packages (server, web, desktop, contracts, shared, client-runtime,
marketing, mobile).
- Validated live in the web client against a seeded copy of real data: pairing, the new
composer placeholder, Settings → Providers showing Cursor/Grok/OpenCode disabled, and the
command palette rendering the new project-location subtitle.
- An xhigh review produced 13 findings. One was confirmed and fixed in `7d0a295db`: the
`#7317` inherited-upstream guard returned without `skipped`, so the caller wiped the
branch's last-known PR and dropped the Merged-badge fallback — the adjacent
unpublished-branch guard sets that flag for exactly this reason. **Worth sending upstream.**
The rest describe upstream design tradeoffs that were adopted as-is rather than rewritten
inside an adoption PR; the notable ones are recorded below.

Open questions raised by this batch, for a later decision:

- **The launchd label is `com.t3tools.t3code.service`.** AGENTS.md forbids renaming
compatibility identifiers during adoption, so it was kept. But Pylon's stated goal is that
Pylon and T3 Code can be installed side by side, and both would now claim the same launch
agent label and TCC records on one Mac. Upstream's own comment says the label is chosen so
those never collide — which only holds for a single product.
- **`QuitHold`** (`#7397`) clears its watchdog on entering `quitOnRelease` without installing
a replacement, so if key events stop arriving mid-hold the "Hold to Quit" overlay can stay
up with no quit.
- **The launchd plist has no `StartLimitBurst` equivalent**, so a server that cannot boot
respawns every 5 seconds indefinitely where systemd gives up after 5 failures in 300s.
- **`backgroundThrottling` now has two independent owners** — `#7460`'s first-reveal trigger
and `#7445`'s frame-capture accounting — with no shared state between them.
- **A muted preview tab loses its speaker affordance** once the guest goes silent
(`tabAudioState` returns "none"), leaving no in-strip way to see or undo the mute.

## Deferred register

_The register is currently empty. DEF-1 and DEF-2 were adopted on 2026-08-11
Expand Down
28 changes: 27 additions & 1 deletion apps/desktop/src/app/DesktopAppIdentity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,9 @@ describe("DesktopAppIdentity", () => {
assert.equal(calls.setAboutPanelOptions[0]?.applicationName, "Pylon (Alpha)");
assert.equal(calls.setAboutPanelOptions[0]?.applicationVersion, "1.2.3");
assert.equal(calls.setAboutPanelOptions[0]?.version, "0123456789ab");
assert.deepEqual(calls.setDockIcon, ["/icon.png"]);
// Packaged: the bundle's own icon stands, so a custom one the user
// attached survives.
assert.deepEqual(calls.setDockIcon, []);
}),
{
calls,
Expand All @@ -221,4 +223,28 @@ describe("DesktopAppIdentity", () => {
},
);
});

it.effect("sets the dock icon only when running unpackaged", () => {
const calls: ElectronAppCalls = {
setAboutPanelOptions: [],
setDockIcon: [],
setName: [],
};

return withIdentity(
Effect.gen(function* () {
const identity = yield* DesktopAppIdentity.DesktopAppIdentity;
yield* identity.configure;

// Electron shows a generic icon for an unpackaged run, which is the
// reason this call exists at all.
assert.deepEqual(calls.setDockIcon, ["/icon.png"]);
}),
{
calls,
environment: { isPackaged: false },
pngIconPath: Option.some("/icon.png"),
},
);
});
});
5 changes: 4 additions & 1 deletion apps/desktop/src/app/DesktopAppIdentity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,10 @@ export const make = Effect.gen(function* () {
yield* electronApp.setDesktopName(environment.linuxDesktopEntryName);
}

if (environment.platform === "darwin") {
// Unpackaged runs only. A packaged bundle already carries its icon in
// Info.plist, so setting the dock tile again changes nothing except to
// overwrite a custom icon the user attached to the app themselves.
if (environment.platform === "darwin" && !environment.isPackaged) {
const iconPaths = yield* assets.iconPaths;
yield* Option.match(iconPaths.png, {
onNone: () => Effect.void,
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/src/ipc/channels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const PREVIEW_ZOOM_OUT_CHANNEL = "desktop:preview-zoom-out";
export const PREVIEW_RESET_ZOOM_CHANNEL = "desktop:preview-reset-zoom";
export const PREVIEW_HARD_RELOAD_CHANNEL = "desktop:preview-hard-reload";
export const PREVIEW_SET_COLOR_SCHEME_CHANNEL = "desktop:preview-set-color-scheme";
export const PREVIEW_SET_AUDIO_MUTED_CHANNEL = "desktop:preview-set-audio-muted";
export const PREVIEW_OPEN_DEVTOOLS_CHANNEL = "desktop:preview-open-devtools";
export const PREVIEW_CLEAR_COOKIES_CHANNEL = "desktop:preview-clear-cookies";
export const PREVIEW_CLEAR_CACHE_CHANNEL = "desktop:preview-clear-cache";
Expand Down
11 changes: 11 additions & 0 deletions apps/desktop/src/ipc/methods/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
DesktopPreviewRecordingSaveInputSchema,
DesktopPreviewRegisterWebviewInputSchema,
DesktopPreviewScreenshotArtifactSchema,
DesktopPreviewSetAudioMutedInputSchema,
DesktopPreviewSetColorSchemeInputSchema,
DesktopPreviewCreateTabInputSchema,
DesktopPreviewTabInputSchema,
Expand Down Expand Up @@ -153,6 +154,15 @@ export const setColorScheme = DesktopIpc.makeIpcMethod({
yield* manager.setColorScheme(tabId, colorScheme);
}),
});
export const setAudioMuted = DesktopIpc.makeIpcMethod({
channel: IpcChannels.PREVIEW_SET_AUDIO_MUTED_CHANNEL,
payload: DesktopPreviewSetAudioMutedInputSchema,
result: Schema.Void,
handler: Effect.fn("desktop.ipc.preview.setAudioMuted")(function* ({ tabId, audioMuted }) {
const manager = yield* PreviewManager.PreviewManager;
yield* manager.setAudioMuted(tabId, audioMuted);
}),
});
export const openDevTools = tabMethod(
IpcChannels.PREVIEW_OPEN_DEVTOOLS_CHANNEL,
"desktop.ipc.preview.openDevTools",
Expand Down Expand Up @@ -372,6 +382,7 @@ export const methods = [
resetZoom,
hardReload,
setColorScheme,
setAudioMuted,
openDevTools,
clearCookies,
clearCache,
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/src/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ contextBridge.exposeInMainWorld("desktopBridge", {
hardReload: (tabId) => ipcRenderer.invoke(IpcChannels.PREVIEW_HARD_RELOAD_CHANNEL, { tabId }),
setColorScheme: (tabId, colorScheme) =>
ipcRenderer.invoke(IpcChannels.PREVIEW_SET_COLOR_SCHEME_CHANNEL, { tabId, colorScheme }),
setAudioMuted: (tabId, audioMuted) =>
ipcRenderer.invoke(IpcChannels.PREVIEW_SET_AUDIO_MUTED_CHANNEL, { tabId, audioMuted }),
openDevTools: (tabId) =>
ipcRenderer.invoke(IpcChannels.PREVIEW_OPEN_DEVTOOLS_CHANNEL, { tabId }),
clearCookies: () => ipcRenderer.invoke(IpcChannels.PREVIEW_CLEAR_COOKIES_CHANNEL),
Expand Down
Loading
Loading