Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
55 changes: 54 additions & 1 deletion .agents/upstream-review.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
remote: t3code-upstream
branch: main
reviewed-through: "2aa5f095fc3bb65c00cc4efce66a5473e2d4554a"
reviewed-through: "f2d5fc91e3030e5c3956fdadc13e1eaa25bcabe3"
reviewed-through-date: "2026-08-19"
---

Expand Down Expand Up @@ -1702,6 +1702,59 @@ Open questions raised by this batch, for a later decision:
- **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.

## 2026-08-19 (second) — `2aa5f095fc3bb65c00cc4efce66a5473e2d4554a..f2d5fc91e3030e5c3956fdadc13e1eaa25bcabe3`

Three upstream commits, three change sets, one PR. **All three adopted** onto
`upstream/2026-08-19-batch2`. `git cherry` reported every one absent from Pylon. The
deferred register was empty going in and stays empty.

**N3 (`#7522`) supersedes work adopted hours earlier in the first 2026-08-19 batch.**
`6a687ee4` had added a local shim in `apps/web/src/main.tsx` overriding
`isAutoFillSupported` to `false`, because `@clerk/electron` reported passkey autofill as
supported while executing the "quiet" request as a modal OS dialog — so the sign-in form
popped a system passkey prompt the moment it mounted. Upstream has now deleted that shim
in favour of a library fix (clerk/javascript#9500).

**The developer chose to adopt it to stay in step with upstream, over a recommendation to
defer.** The trade was stated plainly and decided: Pylon already had correct behavior from
the shim, so adoption buys no user-visible change, and the cost is six **canary**
prereleases pinned to one dated build (`v20260819050620`) — `@clerk/electron`,
`@clerk/electron-passkeys`, and four platform-specific native binaries
(`darwin-arm64`, `darwin-x64`, `win32-arm64-msvc`, `win32-x64-msvc`) — all added to
`minimumReleaseAgeExclude`, plus reliance on the explicitly-internal
`__internal_clerkUIVersion` prop. At adoption time `npm view @clerk/electron dist-tags`
still reported `latest: 0.0.33`; there is no stable `0.0.34`.

**Follow-up this creates:** unpin the canaries once Clerk promotes a stable release
containing PR #9500. Check with `npm view @clerk/electron dist-tags` — when `latest` is
`0.0.34` or newer, move the catalog off the canary strings and drop the six
`minimumReleaseAgeExclude` entries. Until then Pylon ships prerelease native binaries in
its desktop builds.

**N2 (`#6562`)** reorders desktop shutdown so the window closes before cleanup runs
rather than hanging visible through it, threading `ElectronWindow` into lifecycle
registration. It is adjacent to `#7397` (`QuitHold`) adopted in the first batch but does
not address that commit's watchdog gap, which stays open.

**N1 (`#7491`)** is a one-line Tailwind fix adding `flex items-center` so sidebar status
pills align with project names.

Conflicts and adaptations, all resolved Pylon-first:

| File | Conflict | Resolution |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `apps/desktop/src/app/DesktopLifecycle.test.ts` | `#6562` hoists the inline Electron fakes to module-scope helpers and deletes the per-test copies; Pylon's copy carried `name: Effect.succeed("Pylon")` | Took upstream's deletion — the test body below now calls `makeElectronAppLayer(appListeners)` — and restored Pylon's product name on the hoisted fake, which had arrived as "T3 Code". |
| `apps/web/src/components/clerk/electronPasskeys.test.ts` | New test stubs `location` as `t3code:` / `app` with rpId `clerk.t3.codes` | Rewritten to Pylon's `pylon-code:` renderer protocol and a placeholder rpId. Behavior is identical either way: `originCanSatisfyRpId` can never be satisfied by a custom scheme, which is what pushes both assertions onto the native path. |

Validation:

- 3 changed test files, **13 tests, 0 failures** (with the `.prime/` and `.claude/worktrees/`
excludes noted in the previous batch).
- Typecheck clean for `@t3tools/web` and `@t3tools/desktop`, both confirmed to have
actually run rather than filtered to nothing.
- `vp i` resolved the canary native binaries on darwin-arm64 and the lockfile passed the
supply-chain policy check. The other three platform binaries are unverified here.

## Deferred register

_The register is currently empty. DEF-1 and DEF-2 were adopted on 2026-08-11
Expand Down
255 changes: 187 additions & 68 deletions apps/desktop/src/app/DesktopLifecycle.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { assert, describe, it } from "@effect/vitest";
import * as Deferred from "effect/Deferred";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Ref from "effect/Ref";
Expand All @@ -7,91 +8,110 @@ import type * as Electron from "electron";

import * as ElectronApp from "../electron/ElectronApp.ts";
import * as ElectronTheme from "../electron/ElectronTheme.ts";
import * as ElectronWindow from "../electron/ElectronWindow.ts";
import * as DesktopEnvironment from "./DesktopEnvironment.ts";
import * as DesktopLifecycle from "./DesktopLifecycle.ts";
import * as DesktopShutdown from "./DesktopShutdown.ts";
import * as DesktopState from "./DesktopState.ts";
import * as DesktopWindow from "../window/DesktopWindow.ts";

function makeElectronAppLayer(
appListeners: Map<string, (...args: readonly unknown[]) => void>,
quit: Effect.Effect<void> = Effect.void,
) {
const registerListener = (eventName: string, listener: (...args: readonly unknown[]) => void) =>
Effect.acquireRelease(
Effect.sync(() => {
appListeners.set(eventName, listener);
}),
() =>
Effect.sync(() => {
appListeners.delete(eventName);
}),
).pipe(Effect.asVoid);

return Layer.succeed(ElectronApp.ElectronApp, {
metadata: Effect.die("unexpected metadata read"),
name: Effect.succeed("Pylon"),
systemLocale: Effect.succeed("en-US"),
whenReady: Effect.void,
quit,
exit: () => Effect.void,
relaunch: () => Effect.void,
setPath: () => Effect.void,
setName: () => Effect.void,
setAboutPanelOptions: () => Effect.void,
setAppUserModelId: () => Effect.void,
getAppMetrics: Effect.succeed([]),
isDefaultProtocolClient: () => Effect.succeed(false),
setAsDefaultProtocolClient: () => Effect.succeed(true),
setDesktopName: () => Effect.void,
setDockIcon: () => Effect.void,
appendCommandLineSwitch: () => Effect.void,
removeCommandLineSwitch: () => Effect.void,
onBeforeQuitForUpdate: (listener) => registerListener("before-quit-for-update", listener),
on: (eventName, listener) =>
registerListener(eventName, listener as unknown as (...args: readonly unknown[]) => void),
} satisfies ElectronApp.ElectronApp["Service"]);
}

const electronThemeLayer = Layer.succeed(ElectronTheme.ElectronTheme, {
shouldUseDarkColors: Effect.succeed(false),
setSource: () => Effect.void,
onUpdated: () => Effect.void,
});

function makeElectronWindowLayer(destroyAll: Effect.Effect<void> = Effect.void) {
return Layer.succeed(ElectronWindow.ElectronWindow, {
create: () => Effect.die("unexpected window creation"),
main: Effect.die("unexpected main window read"),
currentMainOrFirst: Effect.die("unexpected current window read"),
focusedMainOrFirst: Effect.die("unexpected focused window read"),
setMain: () => Effect.void,
clearMain: () => Effect.void,
reveal: () => Effect.void,
sendAll: () => Effect.void,
destroyAll,
syncAllAppearance: () => Effect.void,
});
}

function makeDesktopWindowLayer(
input: {
readonly activate?: Effect.Effect<void>;
readonly flushMainWindowBounds?: Effect.Effect<void>;
} = {},
) {
return Layer.succeed(DesktopWindow.DesktopWindow, {
createMain: Effect.die("unexpected window creation"),
ensureMain: Effect.die("unexpected window creation"),
revealOrCreateMain: Effect.die("unexpected window creation"),
activate: input.activate ?? Effect.void,
createMainIfBackendReady: Effect.void,
showConnectingSplash: Effect.void,
handleBackendReady: () => Effect.void,
handleBackendNotReady: Effect.void,
flushMainWindowBounds: input.flushMainWindowBounds ?? Effect.void,
dispatchMenuAction: () => Effect.void,
zoomMain: () => Effect.void,
syncAppearance: Effect.void,
});
}

describe("DesktopLifecycle", () => {
for (const platform of ["darwin", "win32", "linux"] satisfies ReadonlyArray<NodeJS.Platform>) {
it.effect(`lets the updater's quit event proceed on ${platform}`, () => {
const appListeners = new Map<string, (...args: readonly unknown[]) => void>();

const electronAppLayer = Layer.succeed(ElectronApp.ElectronApp, {
metadata: Effect.die("unexpected metadata read"),
name: Effect.succeed("Pylon"),
systemLocale: Effect.succeed("en-US"),
whenReady: Effect.void,
quit: Effect.void,
exit: () => Effect.void,
relaunch: () => Effect.void,
setPath: () => Effect.void,
setName: () => Effect.void,
setAboutPanelOptions: () => Effect.void,
setAppUserModelId: () => Effect.void,
getAppMetrics: Effect.succeed([]),
isDefaultProtocolClient: () => Effect.succeed(false),
setAsDefaultProtocolClient: () => Effect.succeed(true),
setDesktopName: () => Effect.void,
setDockIcon: () => Effect.void,
appendCommandLineSwitch: () => Effect.void,
removeCommandLineSwitch: () => Effect.void,
onBeforeQuitForUpdate: (listener) =>
Effect.acquireRelease(
Effect.sync(() => {
appListeners.set("before-quit-for-update", listener);
}),
() =>
Effect.sync(() => {
appListeners.delete("before-quit-for-update");
}),
).pipe(Effect.asVoid),
on: (eventName, listener) =>
Effect.acquireRelease(
Effect.sync(() => {
appListeners.set(
eventName,
listener as unknown as (...args: readonly unknown[]) => void,
);
}),
() =>
Effect.sync(() => {
appListeners.delete(eventName);
}),
).pipe(Effect.asVoid),
} satisfies ElectronApp.ElectronApp["Service"]);

const electronThemeLayer = Layer.succeed(ElectronTheme.ElectronTheme, {
shouldUseDarkColors: Effect.succeed(false),
setSource: () => Effect.void,
onUpdated: () => Effect.void,
});

const desktopWindowLayer = Layer.succeed(DesktopWindow.DesktopWindow, {
createMain: Effect.die("unexpected window creation"),
ensureMain: Effect.die("unexpected window creation"),
revealOrCreateMain: Effect.die("unexpected window creation"),
activate: Effect.void,
createMainIfBackendReady: Effect.void,
showConnectingSplash: Effect.void,
handleBackendReady: () => Effect.void,
handleBackendNotReady: Effect.void,
flushMainWindowBounds: Effect.void,
dispatchMenuAction: () => Effect.void,
zoomMain: () => Effect.void,
syncAppearance: Effect.void,
});

const environmentLayer = Layer.succeed(DesktopEnvironment.DesktopEnvironment, {
platform,
isDevelopment: false,
} as DesktopEnvironment.DesktopEnvironment["Service"]);

const layer = DesktopLifecycle.layer.pipe(
Layer.provideMerge(electronAppLayer),
Layer.provideMerge(makeElectronAppLayer(appListeners)),
Layer.provideMerge(electronThemeLayer),
Layer.provideMerge(desktopWindowLayer),
Layer.provideMerge(makeElectronWindowLayer()),
Layer.provideMerge(makeDesktopWindowLayer()),
Layer.provideMerge(environmentLayer),
Layer.provideMerge(DesktopShutdown.layer),
Layer.provideMerge(DesktopState.layer),
Expand Down Expand Up @@ -123,4 +143,103 @@ describe("DesktopLifecycle", () => {
).pipe(Effect.provide(layer));
});
}

it.effect("destroys windows before waiting for backend shutdown", () =>
Effect.gen(function* () {
const appListeners = new Map<string, (...args: readonly unknown[]) => void>();
const shutdownRequested = yield* Deferred.make<void>();
const allowShutdown = yield* Deferred.make<void>();
const quitRequested = yield* Deferred.make<void>();
const events: string[] = [];

const quit = Effect.sync(() => {
events.push("quit");
}).pipe(Effect.andThen(Deferred.succeed(quitRequested, undefined)), Effect.asVoid);
const destroyAll = Effect.sync(() => {
events.push("destroy");
});
const flushMainWindowBounds = Effect.sync(() => {
events.push("flush");
});

const desktopShutdownLayer = Layer.succeed(DesktopShutdown.DesktopShutdown, {
request: Effect.sync(() => {
events.push("request");
}).pipe(Effect.andThen(Deferred.succeed(shutdownRequested, undefined)), Effect.asVoid),
awaitRequest: Deferred.await(shutdownRequested),
markComplete: Deferred.succeed(allowShutdown, undefined).pipe(Effect.asVoid),
awaitComplete: Deferred.await(allowShutdown),
isComplete: Deferred.isDone(allowShutdown),
});

const environmentLayer = Layer.succeed(DesktopEnvironment.DesktopEnvironment, {
platform: "darwin",
isDevelopment: false,
} as DesktopEnvironment.DesktopEnvironment["Service"]);

const layer = DesktopLifecycle.layer.pipe(
Layer.provideMerge(makeElectronAppLayer(appListeners, quit)),
Layer.provideMerge(electronThemeLayer),
Layer.provideMerge(makeElectronWindowLayer(destroyAll)),
Layer.provideMerge(makeDesktopWindowLayer({ flushMainWindowBounds })),
Layer.provideMerge(environmentLayer),
Layer.provideMerge(desktopShutdownLayer),
Layer.provideMerge(DesktopState.layer),
);

yield* Effect.scoped(
Effect.gen(function* () {
const lifecycle = yield* DesktopLifecycle.DesktopLifecycle;
yield* lifecycle.register;

const event = { preventDefault: () => undefined } as Electron.Event;
appListeners.get("before-quit")?.(event);

yield* Deferred.await(shutdownRequested);
const eventsBeforeCleanup = [...events];
yield* Deferred.succeed(allowShutdown, undefined);
yield* Deferred.await(quitRequested);

assert.deepEqual(eventsBeforeCleanup, ["flush", "destroy", "request"]);
assert.deepEqual(events, ["flush", "destroy", "request", "quit"]);
}),
).pipe(Effect.provide(layer));
}),
);

it.effect("ignores app activation while quitting", () =>
Effect.gen(function* () {
const appListeners = new Map<string, (...args: readonly unknown[]) => void>();
let activationCount = 0;
const activate = Effect.sync(() => {
activationCount += 1;
});
const environmentLayer = Layer.succeed(DesktopEnvironment.DesktopEnvironment, {
platform: "darwin",
isDevelopment: false,
} as DesktopEnvironment.DesktopEnvironment["Service"]);
const layer = DesktopLifecycle.layer.pipe(
Layer.provideMerge(makeElectronAppLayer(appListeners)),
Layer.provideMerge(electronThemeLayer),
Layer.provideMerge(makeElectronWindowLayer()),
Layer.provideMerge(makeDesktopWindowLayer({ activate })),
Layer.provideMerge(environmentLayer),
Layer.provideMerge(DesktopShutdown.layer),
Layer.provideMerge(DesktopState.layer),
);

yield* Effect.scoped(
Effect.gen(function* () {
const lifecycle = yield* DesktopLifecycle.DesktopLifecycle;
const state = yield* DesktopState.DesktopState;
yield* lifecycle.register;
yield* Ref.set(state.quitting, true);

appListeners.get("activate")?.();

assert.equal(activationCount, 0);
}),
).pipe(Effect.provide(layer));
}),
);
});
Loading
Loading