Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
82c2b7f
refactor(web): test environment-scoped draft promotion (#10010)
juliusmarminge Sep 5, 2026
6270a6f
fix(web): retain wrapped row heights during edits (#10018)
juliusmarminge Sep 5, 2026
cb58dfd
refactor(shared): remove unused Clerk hostname predicate (#10008)
juliusmarminge Sep 5, 2026
1d58f2e
refactor(tailscale): keep package internals private (#10011)
juliusmarminge Sep 5, 2026
cd92a7e
ci: reject unused tailscale exports with Knip (#10012)
juliusmarminge Sep 5, 2026
eced382
fix(web): keep chat media at a stable size while it loads (#9938)
juliusmarminge Sep 5, 2026
31fb210
refactor(server): keep manifest age parsing private (#10028)
juliusmarminge Sep 5, 2026
37bf4e6
refactor(mobile): remove unused awareness relay URL normalizer (#10029)
juliusmarminge Sep 5, 2026
f5d9d12
refactor(server): remove unused startup heartbeat launcher (#10030)
juliusmarminge Sep 5, 2026
86f0799
refactor(shared): keep search ranking comparator private (#10031)
juliusmarminge Sep 5, 2026
68aa7aa
refactor(server): keep telemetry identity errors private (#10032)
juliusmarminge Sep 5, 2026
83a2897
refactor(mobile): test composer persistence through the live decoder …
juliusmarminge Sep 5, 2026
5cb6965
refactor(web): remove unused sidebar selectors (#10034)
juliusmarminge Sep 5, 2026
ad3721e
refactor(server): keep Cursor fallback models private (#10038)
juliusmarminge Sep 5, 2026
07fb04d
refactor(web): remove unused xterm link range helpers (#10040)
juliusmarminge Sep 5, 2026
c059d09
refactor(mobile): remove obsolete review list builder (#10039)
juliusmarminge Sep 5, 2026
a21c0d7
test(server): remove duplicate VCS error constructor checks (#10042)
juliusmarminge Sep 5, 2026
5e828bf
refactor(mobile): keep appearance calculations private (#10043)
juliusmarminge Sep 5, 2026
5a4287c
refactor(web): remove unused sidebar menu action (#10044)
juliusmarminge Sep 5, 2026
6615d3d
refactor(web): test live Ghostty link resolution directly (#10041)
juliusmarminge Sep 5, 2026
df370c3
test(server): exercise Codex prompts through public assembly (#10045)
juliusmarminge Sep 5, 2026
62e74cd
refactor(shared): remove unused elapsed-time adapter (#10046)
juliusmarminge Sep 5, 2026
91ba05e
refactor(web): remove unused preview thread reset helper (#10049)
juliusmarminge Sep 5, 2026
56a2f42
refactor(desktop): remove test-only error predicates (#10047)
juliusmarminge Sep 5, 2026
c2aff91
refactor(mobile): keep review reset hashing private (#10048)
juliusmarminge Sep 5, 2026
a98dad7
test(web): remove AppRoot element order snapshot (#10052)
juliusmarminge Sep 5, 2026
62e4ae4
refactor(codex): keep app-server client internals private (#10035)
juliusmarminge Sep 5, 2026
4631000
ci: reject unused Codex client exports with Knip (#10036)
juliusmarminge Sep 5, 2026
45f5a5f
refactor(server): simplify native telemetry error internals (#10057)
juliusmarminge Sep 5, 2026
5b7f6bc
refactor(mobile): remove write-only terminal font cache (#10058)
juliusmarminge Sep 5, 2026
80f775a
refactor(web): remove obsolete HSL theme generator (#10061)
juliusmarminge Sep 5, 2026
d524eb9
refactor(mobile): remove obsolete native diff token stream (#10062)
juliusmarminge Sep 5, 2026
bc24d98
test(server): remove title prompt editorial snapshots (#10063)
juliusmarminge Sep 5, 2026
160e337
test(server): remove repeated runtime prompt interpolation cases (#10…
juliusmarminge Sep 5, 2026
43700b8
refactor(web): observe preview tests through the live registry (#10064)
juliusmarminge Sep 5, 2026
fedf84a
test(server): remove keybinding default assignment snapshot (#10065)
juliusmarminge Sep 5, 2026
4db2c54
refactor(mobile): remove obsolete whole-file review highlighters (#10…
juliusmarminge Sep 5, 2026
0acf05f
test(server): cover CLI runner detection through command suggestions …
juliusmarminge Sep 5, 2026
ee150e1
refactor(mobile): remove unused cloud relay URL normalizer (#10068)
juliusmarminge Sep 5, 2026
009c13f
refactor(web): test live keybinding resolvers directly (#10069)
juliusmarminge Sep 5, 2026
25cbcd6
test(server): cover Grok skill parsing through discovery (#10070)
juliusmarminge Sep 5, 2026
38812c1
test(web): remove mocked diff view prop snapshot (#10073)
juliusmarminge Sep 5, 2026
19ea3c5
test(web): remove mocked annotation options snapshot (#10074)
juliusmarminge Sep 5, 2026
688e594
refactor(web): keep pending action labels private (#10075)
juliusmarminge Sep 5, 2026
b92a812
refactor(mobile): remove unused cloud pending-status mapper (#10071)
juliusmarminge Sep 5, 2026
ab0933a
refactor(web): remove unused model picker hint helpers (#10072)
juliusmarminge Sep 5, 2026
c843c19
fix(server): resume checkpointing after git init (#10078)
Yash-Singh1 Sep 5, 2026
09aac71
feat(web): first-run welcome wizard with agent setup and project impo…
t3dotgg Sep 5, 2026
2271a27
fix(server): keep Homebrew mise shims manual-only (#10085)
juliusmarminge Sep 5, 2026
39802c0
fix(ssh): report remote package installation failures accurately (#10…
juliusmarminge Sep 5, 2026
bd7f7ea
fix(web): keep bulk thread deletion going after failures (#4615)
m-de-graaff Sep 5, 2026
be7796d
fix(web): scale agent spawn rows with interface font (#10092)
juliusmarminge Sep 5, 2026
485782b
fix(web): prevent sidebar tooltip title clipping (#10086)
UtkarshUsername Sep 5, 2026
c1d27e5
fix(web): keep the composer expanded until the thread can scroll (#9965)
t3dotgg Sep 5, 2026
761d4ba
fix(web): preserve original mention text in the composer (#10100)
juliusmarminge Sep 5, 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
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ jobs:
- name: Ensure Electron runtime is installed
run: vp run --filter @t3tools/desktop ensure:electron

# Export cleanup is still a manual audit; files and dependencies have no baseline.
- name: Check unused files and dependencies
# Files/dependencies are repo-wide; export checks cover clean workspaces only.
- name: Check unused code
run: vp run knip:check

- name: Check
Expand Down
3 changes: 0 additions & 3 deletions apps/desktop/src/backend/DesktopServerExposure.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,6 @@ describe("DesktopServerExposure", () => {
modeError,
DesktopServerExposure.DesktopServerExposureModePersistenceError,
);
assert.isTrue(DesktopServerExposure.isDesktopServerExposureSetModeError(modeError));
assert.isTrue(DesktopServerExposure.isDesktopServerExposureError(modeError));
assert.equal(modeError.mode, "network-accessible");
assert.strictEqual(modeError.cause, settingsFailure);
assert.strictEqual(modeError.cause.cause, diskFailure);
Expand All @@ -290,7 +288,6 @@ describe("DesktopServerExposure", () => {
tailscaleError,
DesktopServerExposure.DesktopTailscaleServePersistenceError,
);
assert.isTrue(DesktopServerExposure.isDesktopServerExposureError(tailscaleError));
assert.equal(tailscaleError.enabled, true);
assert.equal(tailscaleError.port, 8443);
assert.strictEqual(tailscaleError.cause, settingsFailure);
Expand Down
2 changes: 0 additions & 2 deletions apps/desktop/src/backend/DesktopServerExposure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,15 +244,13 @@ export const DesktopServerExposureSetModeError = Schema.Union([
DesktopServerExposureModePersistenceError,
]);
export type DesktopServerExposureSetModeError = typeof DesktopServerExposureSetModeError.Type;
export const isDesktopServerExposureSetModeError = Schema.is(DesktopServerExposureSetModeError);

export const DesktopServerExposureError = Schema.Union([
DesktopServerExposureNoNetworkAddressError,
DesktopServerExposureModePersistenceError,
DesktopTailscaleServePersistenceError,
]);
export type DesktopServerExposureError = typeof DesktopServerExposureError.Type;
export const isDesktopServerExposureError = Schema.is(DesktopServerExposureError);

export interface DesktopServerExposureBackendConfig {
readonly port: number;
Expand Down
2 changes: 0 additions & 2 deletions apps/desktop/src/ipc/DesktopIpc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ describe("DesktopIpc", () => {
const error = yield* Effect.flip(Effect.scoped(ipc.handle(invokeMethod)));

assert.instanceOf(error, DesktopIpc.DesktopIpcRegistrationError);
assert.isTrue(DesktopIpc.isDesktopIpcError(error));
assert.strictEqual(error.handlerKind, "invoke");
assert.strictEqual(error.channel, invokeMethod.channel);
assert.strictEqual(error.cause, cause);
Expand Down Expand Up @@ -69,7 +68,6 @@ describe("DesktopIpc", () => {
if (exit._tag === "Success") return;
const error = Cause.squash(exit.cause);
assert.instanceOf(error, DesktopIpc.DesktopIpcUnregistrationError);
assert.isTrue(DesktopIpc.isDesktopIpcError(error));
assert.strictEqual(error.handlerKind, "sync");
assert.strictEqual(error.channel, syncMethod.channel);
assert.strictEqual(error.cause, cause);
Expand Down
1 change: 0 additions & 1 deletion apps/desktop/src/ipc/DesktopIpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ export const DesktopIpcError = Schema.Union([
DesktopIpcUnregistrationError,
]);
export type DesktopIpcError = typeof DesktopIpcError.Type;
export const isDesktopIpcError = Schema.is(DesktopIpcError);

export interface DesktopIpcMethod<E, R> {
readonly channel: string;
Expand Down
6 changes: 0 additions & 6 deletions apps/desktop/src/preview/BrowserSession.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,6 @@ describe("BrowserSession", () => {
const error = yield* browserSessions.getPartition("environment-a").pipe(Effect.flip);

assert.instanceOf(error, BrowserSession.BrowserSessionPartitionDerivationError);
assert.isTrue(BrowserSession.isBrowserSessionGetSessionError(error));
assert.isTrue(BrowserSession.isBrowserSessionError(error));
assert.equal(error.scope, "environment-a");
assert.strictEqual(error.cause, platformCause);
assert.strictEqual(error.cause.reason.cause, nativeCause);
Expand All @@ -196,8 +194,6 @@ describe("BrowserSession", () => {
const error = yield* browserSessions.getSession("environment-b").pipe(Effect.flip);

assert.instanceOf(error, BrowserSession.BrowserSessionCreationError);
assert.isTrue(BrowserSession.isBrowserSessionGetSessionError(error));
assert.isTrue(BrowserSession.isBrowserSessionError(error));
assert.equal(error.scope, "environment-b");
assert.equal(error.partition, partition);
assert.strictEqual(error.cause, cause);
Expand Down Expand Up @@ -270,7 +266,6 @@ describe("BrowserSession", () => {
const storageError = yield* browserSessions.clearCookies().pipe(Effect.flip);

assert.instanceOf(storageError, BrowserSession.BrowserSessionStorageClearError);
assert.isTrue(BrowserSession.isBrowserSessionError(storageError));
assert.equal(storageError.partition, secondPartition);
assert.strictEqual(storageError.cause, storageCause);
assert.equal(
Expand All @@ -287,7 +282,6 @@ describe("BrowserSession", () => {
const cacheError = yield* browserSessions.clearCache().pipe(Effect.flip);

assert.instanceOf(cacheError, BrowserSession.BrowserSessionCacheClearError);
assert.isTrue(BrowserSession.isBrowserSessionError(cacheError));
assert.equal(cacheError.partition, firstPartition);
assert.strictEqual(cacheError.cause, cacheCause);
assert.equal(
Expand Down
2 changes: 0 additions & 2 deletions apps/desktop/src/preview/BrowserSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ export const BrowserSessionGetSessionError = Schema.Union([
BrowserSessionCreationError,
]);
export type BrowserSessionGetSessionError = typeof BrowserSessionGetSessionError.Type;
export const isBrowserSessionGetSessionError = Schema.is(BrowserSessionGetSessionError);

export const BrowserSessionError = Schema.Union([
BrowserSessionPartitionDerivationError,
Expand All @@ -102,7 +101,6 @@ export const BrowserSessionError = Schema.Union([
BrowserSessionCacheClearError,
]);
export type BrowserSessionError = typeof BrowserSessionError.Type;
export const isBrowserSessionError = Schema.is(BrowserSessionError);

export class BrowserSession extends Context.Service<
BrowserSession,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const readWithLogs = (fileSystemLayer: Layer.Layer<FileSystem.FileSystem>) => {
const environment = yield* DesktopEnvironment.DesktopEnvironment;
const settings = yield* DesktopClientSettings.DesktopClientSettings;
return {
result: yield* settings.get,
result: yield* Effect.result(settings.get),
settingsPath: environment.clientSettingsPath,
records,
};
Expand All @@ -73,12 +73,13 @@ describe("DesktopClientSettings diagnostics", () => {
Effect.gen(function* () {
const result = yield* readWithLogs(FileSystem.layerNoop({}));

assert.isTrue(Option.isNone(result.result));
if (result.result._tag !== "Success") return assert.fail("expected a successful read");
assert.isTrue(Option.isNone(result.result.success));
assert.deepEqual(result.records, []);
}),
);

it.effect("logs non-missing filesystem failures with the settings path", () => {
it.effect("reports non-missing filesystem failures and logs the settings path", () => {
const permissionError = PlatformError.systemError({
_tag: "PermissionDenied",
module: "FileSystem",
Expand All @@ -93,7 +94,12 @@ describe("DesktopClientSettings diagnostics", () => {
}),
);

assert.isTrue(Option.isNone(result.result));
if (result.result._tag !== "Failure") return assert.fail("expected a read failure");
assert.instanceOf(
result.result.failure,
DesktopClientSettings.DesktopClientSettingsReadError,
);
assert.strictEqual(result.result.failure.cause, permissionError);
assert.equal(result.records.length, 1);
assert.deepEqual(result.records[0]?.message, [
"Could not read desktop client settings.",
Expand All @@ -103,15 +109,20 @@ describe("DesktopClientSettings diagnostics", () => {
});
});

it.effect("logs malformed settings documents with the settings path", () =>
it.effect("reports malformed settings documents and logs the settings path", () =>
Effect.gen(function* () {
const result = yield* readWithLogs(
FileSystem.layerNoop({
readFileString: () => Effect.succeed("{not-json"),
}),
);

assert.isTrue(Option.isNone(result.result));
if (result.result._tag !== "Failure") return assert.fail("expected a decode failure");
assert.instanceOf(
result.result.failure,
DesktopClientSettings.DesktopClientSettingsReadError,
);
assert.equal(result.result.failure.operation, "decode-document");
assert.equal(result.records.length, 1);
const message = result.records[0]?.message;
if (!Array.isArray(message)) {
Expand Down
92 changes: 80 additions & 12 deletions apps/desktop/src/settings/DesktopClientSettings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const clientSettings: ClientSettings = {
fontSizeTerminal: 12,
fontSmoothing: true,
glassOpacity: 80,
onboardingCompletedAt: null,
panelAnimationDurationMs: 0,
planModeEnabled: false,
proactivePanelsEnabled: true,
Expand Down Expand Up @@ -136,6 +137,59 @@ describe("DesktopClientSettings", () => {
),
);

for (const failure of [
{ label: "permission", reason: "PermissionDenied" },
{ label: "I/O", reason: "Unknown" },
] as const) {
it.effect(`preserves saved preferences across ${failure.label} read failures and retries`, () =>
withClientSettings(
Effect.gen(function* () {
const environment = yield* DesktopEnvironment.DesktopEnvironment;
const fileSystem = yield* FileSystem.FileSystem;
const settings = yield* DesktopClientSettings.DesktopClientSettings;
const savedSettings = {
...clientSettings,
onboardingCompletedAt: "2026-09-05T12:00:00.000Z",
};
yield* settings.set(savedSettings);
const savedContents = yield* fileSystem.readFileString(environment.clientSettingsPath);
const cause = PlatformError.systemError({
_tag: failure.reason,
module: "FileSystem",
method: "readFileString",
pathOrDescriptor: environment.clientSettingsPath,
});
let failRead = true;
const retryableSettings = yield* DesktopClientSettings.make.pipe(
Effect.provideService(
FileSystem.FileSystem,
FileSystem.FileSystem.of({
...fileSystem,
readFileString: (path) =>
Effect.suspend(() =>
failRead ? Effect.fail(cause) : fileSystem.readFileString(path),
),
}),
),
);

const error = yield* retryableSettings.get.pipe(Effect.flip);
assert.instanceOf(error, DesktopClientSettings.DesktopClientSettingsReadError);
assert.equal(error.operation, "read-file");
assert.equal(error.path, environment.clientSettingsPath);
assert.strictEqual(error.cause, cause);
assert.equal(
yield* fileSystem.readFileString(environment.clientSettingsPath),
savedContents,
);

failRead = false;
assert.deepEqual(yield* retryableSettings.get, Option.some(savedSettings));
}),
),
);
}

it.effect("reports the failed client settings write operation and path", () =>
withClientSettings(
Effect.gen(function* () {
Expand Down Expand Up @@ -222,17 +276,31 @@ describe("DesktopClientSettings", () => {
),
);

it.effect("treats malformed client settings documents as absent", () =>
withClientSettings(
Effect.gen(function* () {
const environment = yield* DesktopEnvironment.DesktopEnvironment;
const fileSystem = yield* FileSystem.FileSystem;
const settings = yield* DesktopClientSettings.DesktopClientSettings;
yield* fileSystem.makeDirectory(environment.stateDir, { recursive: true });
yield* fileSystem.writeFileString(environment.clientSettingsPath, "{not-json");
for (const document of [
{ label: "malformed JSON", contents: "{not-json" },
{ label: "invalid direct settings", contents: '{"fontSizeCode":"large"}' },
{ label: "invalid legacy settings", contents: '{"settings":{"fontSizeCode":"large"}}' },
]) {
it.effect(`reports ${document.label} without treating the settings file as absent`, () =>
withClientSettings(
Effect.gen(function* () {
const environment = yield* DesktopEnvironment.DesktopEnvironment;
const fileSystem = yield* FileSystem.FileSystem;
const settings = yield* DesktopClientSettings.DesktopClientSettings;
yield* fileSystem.makeDirectory(environment.stateDir, { recursive: true });
yield* fileSystem.writeFileString(environment.clientSettingsPath, document.contents);

assert.isTrue(Option.isNone(yield* settings.get));
}),
),
);
const error = yield* settings.get.pipe(Effect.flip);
assert.instanceOf(error, DesktopClientSettings.DesktopClientSettingsReadError);
assert.equal(error.operation, "decode-document");
assert.equal(error.path, environment.clientSettingsPath);
assert.instanceOf(error.cause, Schema.SchemaError);
assert.equal(
yield* fileSystem.readFileString(environment.clientSettingsPath),
document.contents,
);
}),
),
);
}
});
60 changes: 42 additions & 18 deletions apps/desktop/src/settings/DesktopClientSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,33 @@ import * as Ref from "effect/Ref";

import * as DesktopEnvironment from "../app/DesktopEnvironment.ts";

const ClientSettingsDocumentSchema = Schema.Struct({
settings: ClientSettingsSchema,
});

const ClientSettingsJson = fromLenientJson(ClientSettingsSchema);
const LegacyClientSettingsDocumentJson = fromLenientJson(ClientSettingsDocumentSchema);
const decodeLegacyClientSettingsDocumentJson = Schema.decodeEffect(
LegacyClientSettingsDocumentJson,
const decodeClientSettingsDocument = Schema.decodeEffect(
fromLenientJson(Schema.Record(Schema.String, Schema.Unknown)),
);
const decodeClientSettingsJsonValue = Schema.decodeEffect(ClientSettingsJson);
const decodeClientSettingsJson = (raw: string): Effect.Effect<ClientSettings, Schema.SchemaError> =>
decodeLegacyClientSettingsDocumentJson(raw).pipe(
Effect.map((document) => document.settings),
Effect.catchTags({
SchemaError: () => decodeClientSettingsJsonValue(raw),
}),
const decodeClientSettingsValue = Schema.decodeUnknownEffect(ClientSettingsSchema);
const decodeClientSettingsJson = Effect.fnUntraced(function* (raw: string) {
const document = yield* decodeClientSettingsDocument(raw);
// Select the shape before validation so invalid legacy settings cannot become defaults.
return yield* decodeClientSettingsValue(
Object.hasOwn(document, "settings") ? document.settings : document,
);
});
const encodeClientSettingsJson = Schema.encodeEffect(ClientSettingsJson);

export class DesktopClientSettingsReadError extends Schema.TaggedErrorClass<DesktopClientSettingsReadError>()(
"DesktopClientSettingsReadError",
{
operation: Schema.Literals(["read-file", "decode-document"]),
path: Schema.String,
cause: Schema.Defect(),
},
) {
override get message(): string {
return `Desktop client settings read failed during ${this.operation} at ${this.path}.`;
}
}

const DesktopClientSettingsWriteOperation = Schema.Literals([
"create-temporary-file-name",
"encode-document",
Expand All @@ -55,7 +63,7 @@ export class DesktopClientSettingsWriteError extends Schema.TaggedErrorClass<Des
export class DesktopClientSettings extends Context.Service<
DesktopClientSettings,
{
readonly get: Effect.Effect<Option.Option<ClientSettings>>;
readonly get: Effect.Effect<Option.Option<ClientSettings>, DesktopClientSettingsReadError>;
readonly set: (
settings: ClientSettings,
) => Effect.Effect<void, DesktopClientSettingsWriteError>;
Expand All @@ -65,7 +73,7 @@ export class DesktopClientSettings extends Context.Service<
const readClientSettings = (
fileSystem: FileSystem.FileSystem,
settingsPath: string,
): Effect.Effect<Option.Option<ClientSettings>> =>
): Effect.Effect<Option.Option<ClientSettings>, DesktopClientSettingsReadError> =>
fileSystem.readFileString(settingsPath).pipe(
Effect.map(Option.some),
Effect.catchTags({
Expand All @@ -74,7 +82,15 @@ const readClientSettings = (
? Effect.succeed(Option.none<string>())
: Effect.logWarning("Could not read desktop client settings.", cause).pipe(
Effect.annotateLogs({ settingsPath }),
Effect.as(Option.none<string>()),
Effect.andThen(
Effect.fail(
new DesktopClientSettingsReadError({
operation: "read-file",
path: settingsPath,
cause,
}),
),
),
),
}),
Effect.flatMap(
Expand All @@ -87,7 +103,15 @@ const readClientSettings = (
SchemaError: (cause) =>
Effect.logWarning("Could not decode desktop client settings.", cause).pipe(
Effect.annotateLogs({ settingsPath }),
Effect.as(Option.none<ClientSettings>()),
Effect.andThen(
Effect.fail(
new DesktopClientSettingsReadError({
operation: "decode-document",
path: settingsPath,
cause,
}),
),
),
),
}),
),
Expand Down
1 change: 0 additions & 1 deletion apps/desktop/src/updates/DesktopUpdates.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,6 @@ describe("DesktopUpdates", () => {
const error = yield* updates.setChannel("nightly").pipe(Effect.flip);

assert.instanceOf(error, DesktopUpdates.DesktopUpdateChannelPersistenceError);
assert.isTrue(DesktopUpdates.isDesktopUpdateSetChannelError(error));
assert.equal(error.channel, "nightly");
assert.strictEqual(error.cause, settingsFailure);
assert.strictEqual(error.cause.cause, diskFailure);
Expand Down
Loading
Loading