Răsfoiți Sursa

test(config): migrate TUI config tests to Effect runner (#27206)

Kit Langton 3 luni în urmă
părinte
comite
7d9ac713a9
1 a modificat fișierele cu 793 adăugiri și 796 ștergeri
  1. 793 796
      packages/opencode/test/config/tui.test.ts

+ 793 - 796
packages/opencode/test/config/tui.test.ts

@@ -1,114 +1,100 @@
-import { afterEach, beforeEach, expect, test } from "bun:test"
+import { expect } from "bun:test"
 import path from "path"
 import path from "path"
-import fs from "fs/promises"
 import { pathToFileURL } from "url"
 import { pathToFileURL } from "url"
-import { provideTestInstance, tmpdir } from "../fixture/fixture"
-import { InstanceRuntime } from "@/project/instance-runtime"
-import { TuiConfig } from "../../src/cli/cmd/tui/config/tui"
-import { Config } from "@/config/config"
-import { Global } from "@opencode-ai/core/global"
-import { Filesystem } from "@/util/filesystem"
-import { AppRuntime } from "../../src/effect/app-runtime"
 import { Effect, Layer } from "effect"
 import { Effect, Layer } from "effect"
-import { CurrentWorkingDirectory } from "@/cli/cmd/tui/config/cwd"
+import { AppFileSystem } from "@opencode-ai/core/filesystem"
+import { Global } from "@opencode-ai/core/global"
+import { Config } from "@/config/config"
 import { ConfigPlugin } from "@/config/plugin"
 import { ConfigPlugin } from "@/config/plugin"
+import { CurrentWorkingDirectory } from "@/cli/cmd/tui/config/cwd"
+import { TuiConfig } from "../../src/cli/cmd/tui/config/tui"
+import { TestInstance } from "../fixture/fixture"
+import { testEffect } from "../lib/effect"
 
 
-const wintest = process.platform === "win32" ? test : test.skip
-const clear = async (wait = false) => {
-  await AppRuntime.runPromise(Config.Service.use((svc) => svc.invalidate()))
-  if (wait) await InstanceRuntime.disposeAllInstances()
-}
-const load = () => AppRuntime.runPromise(Config.Service.use((svc) => svc.get()))
-
-beforeEach(async () => {
-  await clear(true)
-})
-
-const getTuiConfig = async (directory: string) =>
-  Effect.runPromise(
-    TuiConfig.Service.use((svc) => svc.get()).pipe(
-      Effect.provide(TuiConfig.defaultLayer.pipe(Layer.provide(Layer.succeed(CurrentWorkingDirectory, directory)))),
-    ),
-  )
+const it = testEffect(Layer.mergeAll(Config.defaultLayer, AppFileSystem.defaultLayer))
+const winIt = process.platform === "win32" ? it.instance : it.instance.skip
 
 
-async function withPlatform<Value>(platform: typeof process.platform, fn: () => Promise<Value>) {
-  const original = Object.getOwnPropertyDescriptor(process, "platform")
-  Object.defineProperty(process, "platform", {
-    ...original,
-    value: platform,
-  })
-  try {
-    return await fn()
-  } finally {
-    if (original) Object.defineProperty(process, "platform", original)
-  }
-}
+const globalConfigFiles = ["opencode.json", "opencode.jsonc", "tui.json", "tui.jsonc"].map((file) =>
+  path.join(Global.Path.config, file),
+)
 
 
-afterEach(async () => {
+const cleanState = Effect.gen(function* () {
+  const fs = yield* AppFileSystem.Service
   delete process.env.OPENCODE_CONFIG
   delete process.env.OPENCODE_CONFIG
   delete process.env.OPENCODE_TUI_CONFIG
   delete process.env.OPENCODE_TUI_CONFIG
-  await fs.rm(path.join(Global.Path.config, "opencode.json"), { force: true }).catch(() => {})
-  await fs.rm(path.join(Global.Path.config, "opencode.jsonc"), { force: true }).catch(() => {})
-  await fs.rm(path.join(Global.Path.config, "tui.json"), { force: true }).catch(() => {})
-  await fs.rm(path.join(Global.Path.config, "tui.jsonc"), { force: true }).catch(() => {})
-  await clear(true)
+  yield* Effect.forEach(globalConfigFiles, (file) => fs.remove(file, { force: true }).pipe(Effect.ignore), {
+    discard: true,
+  })
 })
 })
 
 
-test("keeps server and tui plugin merge semantics aligned", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      const local = path.join(dir, ".opencode")
-      await fs.mkdir(local, { recursive: true })
-
-      await Bun.write(
-        path.join(Global.Path.config, "opencode.json"),
-        JSON.stringify(
-          {
-            plugin: [["shared-plugin@1.0.0", { source: "global" }], "global-only@1.0.0"],
-          },
-          null,
-          2,
-        ),
-      )
-      await Bun.write(
-        path.join(Global.Path.config, "tui.json"),
-        JSON.stringify(
-          {
-            plugin: [["shared-plugin@1.0.0", { source: "global" }], "global-only@1.0.0"],
-          },
-          null,
-          2,
-        ),
-      )
+const withCleanState = <A, E, R>(self: Effect.Effect<A, E, R>) =>
+  Effect.acquireUseRelease(
+    cleanState,
+    () => self,
+    () => cleanState,
+  )
 
 
-      await Bun.write(
-        path.join(local, "opencode.json"),
-        JSON.stringify(
-          {
-            plugin: [["shared-plugin@2.0.0", { source: "local" }], "local-only@1.0.0"],
-          },
-          null,
-          2,
-        ),
-      )
-      await Bun.write(
-        path.join(local, "tui.json"),
-        JSON.stringify(
-          {
-            plugin: [["shared-plugin@2.0.0", { source: "local" }], "local-only@1.0.0"],
-          },
-          null,
-          2,
-        ),
-      )
-    },
-  })
+const withEnv = <A, E, R>(name: string, value: string | undefined, self: Effect.Effect<A, E, R>) =>
+  Effect.acquireUseRelease(
+    Effect.sync(() => {
+      const previous = process.env[name]
+      if (value === undefined) delete process.env[name]
+      else process.env[name] = value
+      return previous
+    }),
+    () => self,
+    (previous) =>
+      Effect.sync(() => {
+        if (previous === undefined) delete process.env[name]
+        else process.env[name] = previous
+      }),
+  )
+
+const withPlatform = <A, E, R>(platform: typeof process.platform, self: Effect.Effect<A, E, R>) =>
+  Effect.acquireUseRelease(
+    Effect.sync(() => {
+      const original = Object.getOwnPropertyDescriptor(process, "platform")
+      Object.defineProperty(process, "platform", {
+        ...original,
+        value: platform,
+      })
+      return original
+    }),
+    () => self,
+    (original) =>
+      Effect.sync(() => {
+        if (original) Object.defineProperty(process, "platform", original)
+      }),
+  )
 
 
-  await provideTestInstance({
-    directory: tmp.path,
-    fn: async () => {
-      const server = await load()
-      const tui = await getTuiConfig(tmp.path)
+const getTuiConfig = (directory: string) =>
+  TuiConfig.Service.use((svc) => svc.get()).pipe(
+    Effect.provide(TuiConfig.defaultLayer.pipe(Layer.provide(Layer.succeed(CurrentWorkingDirectory, directory)))),
+  )
+
+it.instance("keeps server and tui plugin merge semantics aligned", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      const local = path.join(test.directory, ".opencode")
+      yield* fs.makeDirectory(local, { recursive: true })
+
+      yield* fs.writeJson(path.join(Global.Path.config, "opencode.json"), {
+        plugin: [["shared-plugin@1.0.0", { source: "global" }], "global-only@1.0.0"],
+      })
+      yield* fs.writeJson(path.join(Global.Path.config, "tui.json"), {
+        plugin: [["shared-plugin@1.0.0", { source: "global" }], "global-only@1.0.0"],
+      })
+      yield* fs.writeJson(path.join(local, "opencode.json"), {
+        plugin: [["shared-plugin@2.0.0", { source: "local" }], "local-only@1.0.0"],
+      })
+      yield* fs.writeJson(path.join(local, "tui.json"), {
+        plugin: [["shared-plugin@2.0.0", { source: "local" }], "local-only@1.0.0"],
+      })
+
+      const server = yield* Config.Service.use((svc) => svc.get())
+      const tui = yield* getTuiConfig(test.directory)
       const serverPlugins = (server.plugin ?? []).map((item) => ConfigPlugin.pluginSpecifier(item))
       const serverPlugins = (server.plugin ?? []).map((item) => ConfigPlugin.pluginSpecifier(item))
       const tuiPlugins = (tui.plugin ?? []).map((item) => ConfigPlugin.pluginSpecifier(item))
       const tuiPlugins = (tui.plugin ?? []).map((item) => ConfigPlugin.pluginSpecifier(item))
 
 
@@ -121,239 +107,228 @@ test("keeps server and tui plugin merge semantics aligned", async () => {
       expect(serverOrigins.map((item) => ConfigPlugin.pluginSpecifier(item.spec))).toEqual(serverPlugins)
       expect(serverOrigins.map((item) => ConfigPlugin.pluginSpecifier(item.spec))).toEqual(serverPlugins)
       expect(tuiOrigins.map((item) => ConfigPlugin.pluginSpecifier(item.spec))).toEqual(tuiPlugins)
       expect(tuiOrigins.map((item) => ConfigPlugin.pluginSpecifier(item.spec))).toEqual(tuiPlugins)
       expect(serverOrigins.map((item) => item.scope)).toEqual(tuiOrigins.map((item) => item.scope))
       expect(serverOrigins.map((item) => item.scope)).toEqual(tuiOrigins.map((item) => item.scope))
-    },
-  })
-})
-
-test("loads tui config with the same precedence order as server config paths", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(path.join(Global.Path.config, "tui.json"), JSON.stringify({ theme: "global" }, null, 2))
-      await Bun.write(path.join(dir, "tui.json"), JSON.stringify({ theme: "project" }, null, 2))
-      await fs.mkdir(path.join(dir, ".opencode"), { recursive: true })
-      await Bun.write(
-        path.join(dir, ".opencode", "tui.json"),
+    }),
+  ),
+)
+
+it.instance("loads tui config with the same precedence order as server config paths", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeJson(path.join(Global.Path.config, "tui.json"), { theme: "global" })
+      yield* fs.writeJson(path.join(test.directory, "tui.json"), { theme: "project" })
+      yield* fs.writeWithDirs(
+        path.join(test.directory, ".opencode", "tui.json"),
         JSON.stringify({ theme: "local", diff_style: "stacked" }, null, 2),
         JSON.stringify({ theme: "local", diff_style: "stacked" }, null, 2),
       )
       )
-    },
-  })
-
-  const config = await getTuiConfig(tmp.path)
-  expect(config.theme).toBe("local")
-  expect(config.diff_style).toBe("stacked")
-})
-
-test("resolves attention config defaults and overrides", async () => {
-  await using defaults = await tmpdir()
-  expect((await getTuiConfig(defaults.path)).attention).toEqual({
-    enabled: false,
-    notifications: true,
-    sound: true,
-    volume: 0.4,
-    sound_pack: "opencode.default",
-    sounds: {},
-  })
 
 
-  await using overridden = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(
-        path.join(dir, "tui.json"),
-        JSON.stringify(
-          {
-            attention: {
-              enabled: false,
-              notifications: false,
-              sound: false,
-              volume: 0.7,
-              sound_pack: "acme.soft",
-              sounds: {
-                default: path.join(dir, "default.mp3"),
-                question: pathToFileURL(path.join(dir, "question.mp3")).href,
-                error: "./error.mp3",
-                subagent_done: "./subagent-done.mp3",
-              },
-            },
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.theme).toBe("local")
+      expect(config.diff_style).toBe("stacked")
+    }),
+  ),
+)
+
+it.instance("resolves attention config defaults and overrides", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+
+      expect((yield* getTuiConfig(test.directory)).attention).toEqual({
+        enabled: false,
+        notifications: true,
+        sound: true,
+        volume: 0.4,
+        sound_pack: "opencode.default",
+        sounds: {},
+      })
+
+      yield* fs.writeJson(path.join(test.directory, "tui.json"), {
+        attention: {
+          enabled: false,
+          notifications: false,
+          sound: false,
+          volume: 0.7,
+          sound_pack: "acme.soft",
+          sounds: {
+            default: path.join(test.directory, "default.mp3"),
+            question: pathToFileURL(path.join(test.directory, "question.mp3")).href,
+            error: "./error.mp3",
+            subagent_done: "./subagent-done.mp3",
           },
           },
-          null,
-          2,
-        ),
-      )
-    },
-  })
-
-  expect((await getTuiConfig(overridden.path)).attention).toEqual({
-    enabled: false,
-    notifications: false,
-    sound: false,
-    volume: 0.7,
-    sound_pack: "acme.soft",
-    sounds: {
-      default: path.join(overridden.path, "default.mp3"),
-      question: path.join(overridden.path, "question.mp3"),
-      error: path.join(overridden.path, "error.mp3"),
-      subagent_done: path.join(overridden.path, "subagent-done.mp3"),
-    },
-  })
-})
-
-test("migrates tui-specific keys from opencode.json when tui.json does not exist", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(
-        path.join(dir, "opencode.json"),
-        JSON.stringify(
-          {
-            theme: "migrated-theme",
-            tui: { scroll_speed: 5 },
-            keybinds: { app_exit: "ctrl+q" },
-          },
-          null,
-          2,
-        ),
-      )
-    },
-  })
-
-  const config = await getTuiConfig(tmp.path)
-  expect(config.theme).toBe("migrated-theme")
-  expect(config.scroll_speed).toBe(5)
-  expect(config.keybinds.get("app.exit")?.[0]?.key).toBe("ctrl+q")
-  const text = await Filesystem.readText(path.join(tmp.path, "tui.json"))
-  expect(JSON.parse(text)).toMatchObject({
-    theme: "migrated-theme",
-    scroll_speed: 5,
-  })
-  const server = JSON.parse(await Filesystem.readText(path.join(tmp.path, "opencode.json")))
-  expect(server.theme).toBeUndefined()
-  expect(server.keybinds).toBeUndefined()
-  expect(server.tui).toBeUndefined()
-  expect(await Filesystem.exists(path.join(tmp.path, "opencode.json.tui-migration.bak"))).toBe(true)
-  expect(await Filesystem.exists(path.join(tmp.path, "tui.json"))).toBe(true)
-})
-
-test("migrates project legacy tui keys even when global tui.json already exists", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(path.join(Global.Path.config, "tui.json"), JSON.stringify({ theme: "global" }, null, 2))
-      await Bun.write(
-        path.join(dir, "opencode.json"),
-        JSON.stringify(
-          {
-            theme: "project-migrated",
-            tui: { scroll_speed: 2 },
-          },
-          null,
-          2,
-        ),
-      )
-    },
-  })
-
-  const config = await getTuiConfig(tmp.path)
-  expect(config.theme).toBe("project-migrated")
-  expect(config.scroll_speed).toBe(2)
-  expect(await Filesystem.exists(path.join(tmp.path, "tui.json"))).toBe(true)
-
-  const server = JSON.parse(await Filesystem.readText(path.join(tmp.path, "opencode.json")))
-  expect(server.theme).toBeUndefined()
-  expect(server.tui).toBeUndefined()
-})
-
-test("drops unknown legacy tui keys during migration", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(
-        path.join(dir, "opencode.json"),
-        JSON.stringify(
-          {
-            theme: "migrated-theme",
-            tui: { scroll_speed: 2, foo: 1 },
-          },
-          null,
-          2,
-        ),
-      )
-    },
-  })
-
-  const config = await getTuiConfig(tmp.path)
-  expect(config.theme).toBe("migrated-theme")
-  expect(config.scroll_speed).toBe(2)
-
-  const text = await Filesystem.readText(path.join(tmp.path, "tui.json"))
-  const migrated = JSON.parse(text)
-  expect(migrated.scroll_speed).toBe(2)
-  expect(migrated.foo).toBeUndefined()
-})
-
-test("skips migration when opencode.jsonc is syntactically invalid", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(
-        path.join(dir, "opencode.jsonc"),
+        },
+      })
+
+      expect((yield* getTuiConfig(test.directory)).attention).toEqual({
+        enabled: false,
+        notifications: false,
+        sound: false,
+        volume: 0.7,
+        sound_pack: "acme.soft",
+        sounds: {
+          default: path.join(test.directory, "default.mp3"),
+          question: path.join(test.directory, "question.mp3"),
+          error: path.join(test.directory, "error.mp3"),
+          subagent_done: path.join(test.directory, "subagent-done.mp3"),
+        },
+      })
+    }),
+  ),
+)
+
+it.instance("migrates tui-specific keys from opencode.json when tui.json does not exist", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      const source = path.join(test.directory, "opencode.json")
+      yield* fs.writeJson(source, {
+        theme: "migrated-theme",
+        tui: { scroll_speed: 5 },
+        keybinds: { app_exit: "ctrl+q" },
+      })
+
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.theme).toBe("migrated-theme")
+      expect(config.scroll_speed).toBe(5)
+      expect(config.keybinds.get("app.exit")?.[0]?.key).toBe("ctrl+q")
+      expect(JSON.parse(yield* fs.readFileString(path.join(test.directory, "tui.json")))).toMatchObject({
+        theme: "migrated-theme",
+        scroll_speed: 5,
+      })
+      const server = JSON.parse(yield* fs.readFileString(source))
+      expect(server.theme).toBeUndefined()
+      expect(server.keybinds).toBeUndefined()
+      expect(server.tui).toBeUndefined()
+      expect(yield* fs.existsSafe(path.join(test.directory, "opencode.json.tui-migration.bak"))).toBe(true)
+      expect(yield* fs.existsSafe(path.join(test.directory, "tui.json"))).toBe(true)
+    }),
+  ),
+)
+
+it.instance("migrates project legacy tui keys even when global tui.json already exists", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeJson(path.join(Global.Path.config, "tui.json"), { theme: "global" })
+      yield* fs.writeJson(path.join(test.directory, "opencode.json"), {
+        theme: "project-migrated",
+        tui: { scroll_speed: 2 },
+      })
+
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.theme).toBe("project-migrated")
+      expect(config.scroll_speed).toBe(2)
+      expect(yield* fs.existsSafe(path.join(test.directory, "tui.json"))).toBe(true)
+
+      const server = JSON.parse(yield* fs.readFileString(path.join(test.directory, "opencode.json")))
+      expect(server.theme).toBeUndefined()
+      expect(server.tui).toBeUndefined()
+    }),
+  ),
+)
+
+it.instance("drops unknown legacy tui keys during migration", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeJson(path.join(test.directory, "opencode.json"), {
+        theme: "migrated-theme",
+        tui: { scroll_speed: 2, foo: 1 },
+      })
+
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.theme).toBe("migrated-theme")
+      expect(config.scroll_speed).toBe(2)
+
+      const migrated = JSON.parse(yield* fs.readFileString(path.join(test.directory, "tui.json")))
+      expect(migrated.scroll_speed).toBe(2)
+      expect(migrated.foo).toBeUndefined()
+    }),
+  ),
+)
+
+it.instance("skips migration when opencode.jsonc is syntactically invalid", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeFileString(
+        path.join(test.directory, "opencode.jsonc"),
         `{
         `{
   "theme": "broken-theme",
   "theme": "broken-theme",
   "tui": { "scroll_speed": 2 }
   "tui": { "scroll_speed": 2 }
   "username": "still-broken"
   "username": "still-broken"
 }`,
 }`,
       )
       )
-    },
-  })
-
-  const config = await getTuiConfig(tmp.path)
-  expect(config.theme).toBeUndefined()
-  expect(config.scroll_speed).toBeUndefined()
-  expect(await Filesystem.exists(path.join(tmp.path, "tui.json"))).toBe(false)
-  expect(await Filesystem.exists(path.join(tmp.path, "opencode.jsonc.tui-migration.bak"))).toBe(false)
-  const source = await Filesystem.readText(path.join(tmp.path, "opencode.jsonc"))
-  expect(source).toContain('"theme": "broken-theme"')
-  expect(source).toContain('"tui": { "scroll_speed": 2 }')
-})
-
-test("skips migration when tui.json already exists", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(path.join(dir, "opencode.json"), JSON.stringify({ theme: "legacy" }, null, 2))
-      await Bun.write(path.join(dir, "tui.json"), JSON.stringify({ diff_style: "stacked" }, null, 2))
-    },
-  })
-
-  const config = await getTuiConfig(tmp.path)
-  expect(config.diff_style).toBe("stacked")
-  expect(config.theme).toBeUndefined()
-
-  const server = JSON.parse(await Filesystem.readText(path.join(tmp.path, "opencode.json")))
-  expect(server.theme).toBe("legacy")
-  expect(await Filesystem.exists(path.join(tmp.path, "opencode.json.tui-migration.bak"))).toBe(false)
-})
-
-test("continues loading tui config when legacy source cannot be stripped", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(path.join(dir, "opencode.json"), JSON.stringify({ theme: "readonly-theme" }, null, 2))
-    },
-  })
 
 
-  const source = path.join(tmp.path, "opencode.json")
-  await fs.chmod(source, 0o444)
-
-  try {
-    const config = await getTuiConfig(tmp.path)
-    expect(config.theme).toBe("readonly-theme")
-    expect(await Filesystem.exists(path.join(tmp.path, "tui.json"))).toBe(true)
-
-    const server = JSON.parse(await Filesystem.readText(source))
-    expect(server.theme).toBe("readonly-theme")
-  } finally {
-    await fs.chmod(source, 0o644)
-  }
-})
-
-test("migration backup preserves JSONC comments", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(
-        path.join(dir, "opencode.jsonc"),
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.theme).toBeUndefined()
+      expect(config.scroll_speed).toBeUndefined()
+      expect(yield* fs.existsSafe(path.join(test.directory, "tui.json"))).toBe(false)
+      expect(yield* fs.existsSafe(path.join(test.directory, "opencode.jsonc.tui-migration.bak"))).toBe(false)
+      const source = yield* fs.readFileString(path.join(test.directory, "opencode.jsonc"))
+      expect(source).toContain('"theme": "broken-theme"')
+      expect(source).toContain('"tui": { "scroll_speed": 2 }')
+    }),
+  ),
+)
+
+it.instance("skips migration when tui.json already exists", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeJson(path.join(test.directory, "opencode.json"), { theme: "legacy" })
+      yield* fs.writeJson(path.join(test.directory, "tui.json"), { diff_style: "stacked" })
+
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.diff_style).toBe("stacked")
+      expect(config.theme).toBeUndefined()
+
+      const server = JSON.parse(yield* fs.readFileString(path.join(test.directory, "opencode.json")))
+      expect(server.theme).toBe("legacy")
+      expect(yield* fs.existsSafe(path.join(test.directory, "opencode.json.tui-migration.bak"))).toBe(false)
+    }),
+  ),
+)
+
+it.instance("continues loading tui config when legacy source cannot be stripped", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      const source = path.join(test.directory, "opencode.json")
+      yield* fs.writeJson(source, { theme: "readonly-theme" })
+
+      yield* Effect.acquireUseRelease(
+        fs.chmod(source, 0o444),
+        () =>
+          Effect.gen(function* () {
+            const config = yield* getTuiConfig(test.directory)
+            expect(config.theme).toBe("readonly-theme")
+            expect(yield* fs.existsSafe(path.join(test.directory, "tui.json"))).toBe(true)
+
+            const server = JSON.parse(yield* fs.readFileString(source))
+            expect(server.theme).toBe("readonly-theme")
+          }),
+        () => fs.chmod(source, 0o644).pipe(Effect.ignore),
+      )
+    }),
+  ),
+)
+
+it.instance("migration backup preserves JSONC comments", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeFileString(
+        path.join(test.directory, "opencode.jsonc"),
         `{
         `{
   // top-level comment
   // top-level comment
   "theme": "jsonc-theme",
   "theme": "jsonc-theme",
@@ -363,498 +338,520 @@ test("migration backup preserves JSONC comments", async () => {
   }
   }
 }`,
 }`,
       )
       )
-    },
-  })
-
-  await getTuiConfig(tmp.path)
-  const backup = await Filesystem.readText(path.join(tmp.path, "opencode.jsonc.tui-migration.bak"))
-  expect(backup).toContain("// top-level comment")
-  expect(backup).toContain("// nested comment")
-  expect(backup).toContain('"theme": "jsonc-theme"')
-  expect(backup).toContain('"scroll_speed": 1.5')
-})
-
-test("migrates legacy tui keys across multiple opencode.json levels", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      const nested = path.join(dir, "apps", "client")
-      await fs.mkdir(nested, { recursive: true })
-      await Bun.write(path.join(dir, "opencode.json"), JSON.stringify({ theme: "root-theme" }, null, 2))
-      await Bun.write(path.join(nested, "opencode.json"), JSON.stringify({ theme: "nested-theme" }, null, 2))
-    },
-  })
-  const config = await getTuiConfig(path.join(tmp.path, "apps", "client"))
-  expect(config.theme).toBe("nested-theme")
-  expect(await Filesystem.exists(path.join(tmp.path, "tui.json"))).toBe(true)
-  expect(await Filesystem.exists(path.join(tmp.path, "apps", "client", "tui.json"))).toBe(true)
-})
 
 
-test("flattens nested tui key inside tui.json", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(
-        path.join(dir, "tui.json"),
-        JSON.stringify({
-          theme: "outer",
-          tui: { scroll_speed: 3, diff_style: "stacked" },
+      yield* getTuiConfig(test.directory)
+      const backup = yield* fs.readFileString(path.join(test.directory, "opencode.jsonc.tui-migration.bak"))
+      expect(backup).toContain("// top-level comment")
+      expect(backup).toContain("// nested comment")
+      expect(backup).toContain('"theme": "jsonc-theme"')
+      expect(backup).toContain('"scroll_speed": 1.5')
+    }),
+  ),
+)
+
+it.instance("migrates legacy tui keys across multiple opencode.json levels", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      const nested = path.join(test.directory, "apps", "client")
+      yield* fs.makeDirectory(nested, { recursive: true })
+      yield* fs.writeJson(path.join(test.directory, "opencode.json"), { theme: "root-theme" })
+      yield* fs.writeJson(path.join(nested, "opencode.json"), { theme: "nested-theme" })
+
+      const config = yield* getTuiConfig(nested)
+      expect(config.theme).toBe("nested-theme")
+      expect(yield* fs.existsSafe(path.join(test.directory, "tui.json"))).toBe(true)
+      expect(yield* fs.existsSafe(path.join(nested, "tui.json"))).toBe(true)
+    }),
+  ),
+)
+
+it.instance("flattens nested tui key inside tui.json", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeJson(path.join(test.directory, "tui.json"), {
+        theme: "outer",
+        tui: { scroll_speed: 3, diff_style: "stacked" },
+      })
+
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.scroll_speed).toBe(3)
+      expect(config.diff_style).toBe("stacked")
+      expect(config.theme).toBe("outer")
+    }),
+  ),
+)
+
+it.instance("top-level keys in tui.json take precedence over nested tui key", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeJson(path.join(test.directory, "tui.json"), {
+        diff_style: "auto",
+        tui: { diff_style: "stacked", scroll_speed: 2 },
+      })
+
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.diff_style).toBe("auto")
+      expect(config.scroll_speed).toBe(2)
+    }),
+  ),
+)
+
+it.instance("project config takes precedence over OPENCODE_TUI_CONFIG (matches OPENCODE_CONFIG)", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      const custom = path.join(test.directory, "custom-tui.json")
+      yield* fs.writeJson(path.join(test.directory, "tui.json"), { theme: "project", diff_style: "auto" })
+      yield* fs.writeJson(custom, { theme: "custom", diff_style: "stacked" })
+
+      yield* withEnv(
+        "OPENCODE_TUI_CONFIG",
+        custom,
+        Effect.gen(function* () {
+          const config = yield* getTuiConfig(test.directory)
+          expect(config.theme).toBe("project")
+          expect(config.diff_style).toBe("auto")
         }),
         }),
       )
       )
-    },
-  })
-
-  const config = await getTuiConfig(tmp.path)
-  expect(config.scroll_speed).toBe(3)
-  expect(config.diff_style).toBe("stacked")
-  // top-level keys take precedence over nested tui keys
-  expect(config.theme).toBe("outer")
-})
-
-test("top-level keys in tui.json take precedence over nested tui key", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(
-        path.join(dir, "tui.json"),
-        JSON.stringify({
-          diff_style: "auto",
-          tui: { diff_style: "stacked", scroll_speed: 2 },
-        }),
+    }),
+  ),
+)
+
+it.instance("merges keybind overrides across precedence layers", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeJson(path.join(Global.Path.config, "tui.json"), { keybinds: { app_exit: "ctrl+q" } })
+      yield* fs.writeJson(path.join(test.directory, "tui.json"), { keybinds: { theme_list: "ctrl+k" } })
+
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.keybinds.get("app.exit")?.[0]?.key).toBe("ctrl+q")
+      expect(config.keybinds.get("theme.switch")?.[0]?.key).toBe("ctrl+k")
+    }),
+  ),
+)
+
+it.instance("resolves keybind lookup from canonical keybinds", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeJson(path.join(test.directory, "tui.json"), {
+        keybinds: {
+          leader: { key: { name: "g", ctrl: true } },
+          command_list: "alt+p",
+          which_key_toggle: "alt+k",
+          editor_open: "ctrl+e",
+          "prompt.autocomplete.next": "ctrl+j",
+          "dialog.mcp.toggle": "ctrl+t",
+          model_favorite_toggle: "ctrl+f",
+          "dialog.plugins.install": "shift+i",
+        },
+        leader_timeout: 1234,
+      })
+
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.keybinds.get("leader")?.[0]?.key).toEqual({ name: "g", ctrl: true })
+      expect(config.leader_timeout).toBe(1234)
+      expect(config.keybinds.get("command.palette.show")?.[0]?.key).toBe("alt+p")
+      expect(config.keybinds.get("session.new")?.[0]?.key).toBe("<leader>n")
+      expect(config.keybinds.get("which-key.toggle")?.[0]?.key).toBe("alt+k")
+      expect(config.keybinds.get("which-key.layout.toggle")?.[0]?.key).toBe("ctrl+alt+shift+k")
+      expect(config.keybinds.get("which-key.pending.toggle")?.[0]?.key).toBe("ctrl+alt+shift+p")
+      expect(config.keybinds.get("which-key.group.next")?.[0]?.key).toBe("ctrl+alt+right,ctrl+alt+]")
+      expect((config.keybinds.get("which-key.toggle")?.[0] as { desc?: unknown } | undefined)?.desc).toBe(
+        "Toggle which-key panel",
       )
       )
-    },
-  })
-
-  const config = await getTuiConfig(tmp.path)
-  expect(config.diff_style).toBe("auto")
-  expect(config.scroll_speed).toBe(2)
-})
-
-test("project config takes precedence over OPENCODE_TUI_CONFIG (matches OPENCODE_CONFIG)", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(path.join(dir, "tui.json"), JSON.stringify({ theme: "project", diff_style: "auto" }))
-      const custom = path.join(dir, "custom-tui.json")
-      await Bun.write(custom, JSON.stringify({ theme: "custom", diff_style: "stacked" }))
-      process.env.OPENCODE_TUI_CONFIG = custom
-    },
-  })
-
-  const config = await getTuiConfig(tmp.path)
-  // project tui.json overrides the custom path, same as server config precedence
-  expect(config.theme).toBe("project")
-  // project also set diff_style, so that wins
-  expect(config.diff_style).toBe("auto")
-})
-
-test("merges keybind overrides across precedence layers", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(path.join(Global.Path.config, "tui.json"), JSON.stringify({ keybinds: { app_exit: "ctrl+q" } }))
-      await Bun.write(path.join(dir, "tui.json"), JSON.stringify({ keybinds: { theme_list: "ctrl+k" } }))
-    },
-  })
-  const config = await getTuiConfig(tmp.path)
-  expect(config.keybinds.get("app.exit")?.[0]?.key).toBe("ctrl+q")
-  expect(config.keybinds.get("theme.switch")?.[0]?.key).toBe("ctrl+k")
-})
-
-test("resolves keybind lookup from canonical keybinds", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(
-        path.join(dir, "tui.json"),
-        JSON.stringify({
+      expect(config.keybinds.get("prompt.editor")?.[0]?.key).toBe("ctrl+e")
+      expect(config.keybinds.get("prompt.autocomplete.next")?.[0]?.key).toBe("ctrl+j")
+      expect(config.keybinds.get("dialog.mcp.toggle")?.[0]?.key).toBe("ctrl+t")
+      expect(config.keybinds.get("model.dialog.favorite")?.[0]?.key).toBe("ctrl+f")
+      expect(config.keybinds.get("dialog.plugins.install")?.[0]?.key).toBe("shift+i")
+      expect(config.keybinds.gather("plugins.dialog", ["dialog.plugins.install"]).map((binding) => binding.cmd)).toEqual([
+        "dialog.plugins.install",
+      ])
+    }),
+  ),
+)
+
+it.instance("keybinds accept OpenTUI binding specs", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeJson(path.join(test.directory, "tui.json"), {
+        keybinds: {
+          command_list: [{ key: "alt+p", preventDefault: false }],
+          editor_open: { key: { name: "e", ctrl: true }, group: "Explicit" },
+          "prompt.autocomplete.next": false,
+          plugin_manager: "ctrl+shift+p",
+        },
+      })
+
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.keybinds.get("command.palette.show")).toEqual([
+        { key: "alt+p", cmd: "command.palette.show", preventDefault: false, desc: "List available commands" },
+      ])
+      expect(config.keybinds.get("prompt.editor")?.[0]).toMatchObject({
+        key: { name: "e", ctrl: true },
+        cmd: "prompt.editor",
+        group: "Explicit",
+      })
+      expect(config.keybinds.get("prompt.autocomplete.next")).toEqual([])
+      expect(config.keybinds.get("plugins.list")?.[0]?.key).toBe("ctrl+shift+p")
+    }),
+  ),
+)
+
+winIt("defaults Ctrl+Z to input undo on Windows", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const test = yield* TestInstance
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.keybinds.get("terminal.suspend")).toEqual([])
+      expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+z,ctrl+-,super+z")
+    }),
+  ),
+)
+
+winIt("keeps explicit input undo overrides on Windows", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeJson(path.join(test.directory, "tui.json"), { keybinds: { input_undo: "ctrl+y" } })
+
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.keybinds.get("terminal.suspend")).toEqual([])
+      expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+y")
+    }),
+  ),
+)
+
+winIt("ignores terminal suspend bindings on Windows", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeJson(path.join(test.directory, "tui.json"), { keybinds: { terminal_suspend: "alt+z" } })
+
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.keybinds.get("terminal.suspend")).toEqual([])
+      expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+z,ctrl+-,super+z")
+    }),
+  ),
+)
+
+it.instance("applies Windows keybind defaults", () =>
+  withCleanState(
+    withPlatform(
+      "win32",
+      Effect.gen(function* () {
+        const test = yield* TestInstance
+        const config = yield* getTuiConfig(test.directory)
+        expect(config.keybinds.get("terminal.suspend")).toEqual([])
+        expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+z,ctrl+-,super+z")
+      }),
+    ),
+  ),
+)
+
+it.instance("ignores explicit keybind terminal suspend binding on Windows", () =>
+  withCleanState(
+    withPlatform(
+      "win32",
+      Effect.gen(function* () {
+        const fs = yield* AppFileSystem.Service
+        const test = yield* TestInstance
+        yield* fs.writeJson(path.join(test.directory, "tui.json"), {
           keybinds: {
           keybinds: {
-            leader: { key: { name: "g", ctrl: true } },
-            command_list: "alt+p",
-            which_key_toggle: "alt+k",
-            editor_open: "ctrl+e",
-            "prompt.autocomplete.next": "ctrl+j",
-            "dialog.mcp.toggle": "ctrl+t",
-            model_favorite_toggle: "ctrl+f",
-            "dialog.plugins.install": "shift+i",
+            terminal_suspend: "alt+z",
           },
           },
-          leader_timeout: 1234,
-        }),
-      )
-    },
-  })
-
-  const config = await getTuiConfig(tmp.path)
-  expect(config.keybinds.get("leader")?.[0]?.key).toEqual({ name: "g", ctrl: true })
-  expect(config.leader_timeout).toBe(1234)
-  expect(config.keybinds.get("command.palette.show")?.[0]?.key).toBe("alt+p")
-  expect(config.keybinds.get("session.new")?.[0]?.key).toBe("<leader>n")
-  expect(config.keybinds.get("which-key.toggle")?.[0]?.key).toBe("alt+k")
-  expect(config.keybinds.get("which-key.layout.toggle")?.[0]?.key).toBe("ctrl+alt+shift+k")
-  expect(config.keybinds.get("which-key.pending.toggle")?.[0]?.key).toBe("ctrl+alt+shift+p")
-  expect(config.keybinds.get("which-key.group.next")?.[0]?.key).toBe("ctrl+alt+right,ctrl+alt+]")
-  expect((config.keybinds.get("which-key.toggle")?.[0] as { desc?: unknown } | undefined)?.desc).toBe(
-    "Toggle which-key panel",
-  )
-  expect(config.keybinds.get("prompt.editor")?.[0]?.key).toBe("ctrl+e")
-  expect(config.keybinds.get("prompt.autocomplete.next")?.[0]?.key).toBe("ctrl+j")
-  expect(config.keybinds.get("dialog.mcp.toggle")?.[0]?.key).toBe("ctrl+t")
-  expect(config.keybinds.get("model.dialog.favorite")?.[0]?.key).toBe("ctrl+f")
-  expect(config.keybinds.get("dialog.plugins.install")?.[0]?.key).toBe("shift+i")
-  expect(config.keybinds.gather("plugins.dialog", ["dialog.plugins.install"]).map((binding) => binding.cmd)).toEqual([
-    "dialog.plugins.install",
-  ])
-})
+        })
 
 
-test("keybinds accept OpenTUI binding specs", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(
-        path.join(dir, "tui.json"),
-        JSON.stringify({
+        const config = yield* getTuiConfig(test.directory)
+        expect(config.keybinds.get("terminal.suspend")).toEqual([])
+      }),
+    ),
+  ),
+)
+
+it.instance("keeps explicit configured keybind input undo on Windows", () =>
+  withCleanState(
+    withPlatform(
+      "win32",
+      Effect.gen(function* () {
+        const fs = yield* AppFileSystem.Service
+        const test = yield* TestInstance
+        yield* fs.writeJson(path.join(test.directory, "tui.json"), {
           keybinds: {
           keybinds: {
-            command_list: [{ key: "alt+p", preventDefault: false }],
-            editor_open: { key: { name: "e", ctrl: true }, group: "Explicit" },
-            "prompt.autocomplete.next": false,
-            plugin_manager: "ctrl+shift+p",
+            input_undo: "ctrl+y",
           },
           },
+        })
+
+        const config = yield* getTuiConfig(test.directory)
+        expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+y")
+      }),
+    ),
+  ),
+)
+
+it.instance("OPENCODE_TUI_CONFIG provides settings when no project config exists", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      const custom = path.join(test.directory, "custom-tui.json")
+      yield* fs.writeJson(custom, { theme: "from-env", diff_style: "stacked" })
+
+      yield* withEnv(
+        "OPENCODE_TUI_CONFIG",
+        custom,
+        Effect.gen(function* () {
+          const config = yield* getTuiConfig(test.directory)
+          expect(config.theme).toBe("from-env")
+          expect(config.diff_style).toBe("stacked")
         }),
         }),
       )
       )
-    },
-  })
-
-  const config = await getTuiConfig(tmp.path)
-  expect(config.keybinds.get("command.palette.show")).toEqual([
-    { key: "alt+p", cmd: "command.palette.show", preventDefault: false, desc: "List available commands" },
-  ])
-  expect(config.keybinds.get("prompt.editor")?.[0]).toMatchObject({
-    key: { name: "e", ctrl: true },
-    cmd: "prompt.editor",
-    group: "Explicit",
-  })
-  expect(config.keybinds.get("prompt.autocomplete.next")).toEqual([])
-  expect(config.keybinds.get("plugins.list")?.[0]?.key).toBe("ctrl+shift+p")
-})
-
-wintest("defaults Ctrl+Z to input undo on Windows", async () => {
-  await using tmp = await tmpdir()
-  const config = await getTuiConfig(tmp.path)
-  expect(config.keybinds.get("terminal.suspend")).toEqual([])
-  expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+z,ctrl+-,super+z")
-})
-
-wintest("keeps explicit input undo overrides on Windows", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(path.join(dir, "tui.json"), JSON.stringify({ keybinds: { input_undo: "ctrl+y" } }))
-    },
-  })
-  const config = await getTuiConfig(tmp.path)
-  expect(config.keybinds.get("terminal.suspend")).toEqual([])
-  expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+y")
-})
-
-wintest("ignores terminal suspend bindings on Windows", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(path.join(dir, "tui.json"), JSON.stringify({ keybinds: { terminal_suspend: "alt+z" } }))
-    },
-  })
-
-  const config = await getTuiConfig(tmp.path)
-  expect(config.keybinds.get("terminal.suspend")).toEqual([])
-  expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+z,ctrl+-,super+z")
-})
-
-test("applies Windows keybind defaults", async () => {
-  await withPlatform("win32", async () => {
-    await using tmp = await tmpdir()
-
-    const config = await getTuiConfig(tmp.path)
-    expect(config.keybinds.get("terminal.suspend")).toEqual([])
-    expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+z,ctrl+-,super+z")
-  })
-})
-
-test("ignores explicit keybind terminal suspend binding on Windows", async () => {
-  await withPlatform("win32", async () => {
-    await using tmp = await tmpdir({
-      init: async (dir) => {
-        await Bun.write(
-          path.join(dir, "tui.json"),
-          JSON.stringify({
-            keybinds: {
-              terminal_suspend: "alt+z",
-            },
-          }),
-        )
-      },
-    })
-
-    const config = await getTuiConfig(tmp.path)
-    expect(config.keybinds.get("terminal.suspend")).toEqual([])
-  })
-})
-
-test("keeps explicit configured keybind input undo on Windows", async () => {
-  await withPlatform("win32", async () => {
-    await using tmp = await tmpdir({
-      init: async (dir) => {
-        await Bun.write(
-          path.join(dir, "tui.json"),
-          JSON.stringify({
-            keybinds: {
-              input_undo: "ctrl+y",
-            },
-          }),
-        )
-      },
-    })
-
-    const config = await getTuiConfig(tmp.path)
-    expect(config.keybinds.get("input.undo")?.[0]?.key).toBe("ctrl+y")
-  })
-})
-
-test("OPENCODE_TUI_CONFIG provides settings when no project config exists", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      const custom = path.join(dir, "custom-tui.json")
-      await Bun.write(custom, JSON.stringify({ theme: "from-env", diff_style: "stacked" }))
-      process.env.OPENCODE_TUI_CONFIG = custom
-    },
-  })
-  const config = await getTuiConfig(tmp.path)
-  expect(config.theme).toBe("from-env")
-  expect(config.diff_style).toBe("stacked")
-})
-
-test("does not derive tui path from OPENCODE_CONFIG", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      const customDir = path.join(dir, "custom")
-      await fs.mkdir(customDir, { recursive: true })
-      await Bun.write(path.join(customDir, "opencode.json"), JSON.stringify({ model: "test/model" }))
-      await Bun.write(path.join(customDir, "tui.json"), JSON.stringify({ theme: "should-not-load" }))
-      process.env.OPENCODE_CONFIG = path.join(customDir, "opencode.json")
-    },
-  })
-  const config = await getTuiConfig(tmp.path)
-  expect(config.theme).toBeUndefined()
-})
-
-test("applies env and file substitutions in tui.json", async () => {
-  const original = process.env.TUI_THEME_TEST
-  process.env.TUI_THEME_TEST = "env-theme"
-  try {
-    await using tmp = await tmpdir({
-      init: async (dir) => {
-        await Bun.write(path.join(dir, "keybind.txt"), "ctrl+q")
-        await Bun.write(
-          path.join(dir, "tui.json"),
-          JSON.stringify({
-            theme: "{env:TUI_THEME_TEST}",
-            keybinds: { app_exit: "{file:keybind.txt}" },
-          }),
-        )
-      },
-    })
-    const config = await getTuiConfig(tmp.path)
-    expect(config.theme).toBe("env-theme")
-    expect(config.keybinds.get("app.exit")?.[0]?.key).toBe("ctrl+q")
-  } finally {
-    if (original === undefined) delete process.env.TUI_THEME_TEST
-    else process.env.TUI_THEME_TEST = original
-  }
-})
-
-test("applies file substitutions when first identical token is in a commented line", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(path.join(dir, "theme.txt"), "resolved-theme")
-      await Bun.write(
-        path.join(dir, "tui.jsonc"),
+    }),
+  ),
+)
+
+it.instance("does not derive tui path from OPENCODE_CONFIG", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      const customDir = path.join(test.directory, "custom")
+      yield* fs.makeDirectory(customDir, { recursive: true })
+      yield* fs.writeJson(path.join(customDir, "opencode.json"), { model: "test/model" })
+      yield* fs.writeJson(path.join(customDir, "tui.json"), { theme: "should-not-load" })
+
+      yield* withEnv(
+        "OPENCODE_CONFIG",
+        path.join(customDir, "opencode.json"),
+        Effect.gen(function* () {
+          const config = yield* getTuiConfig(test.directory)
+          expect(config.theme).toBeUndefined()
+        }),
+      )
+    }),
+  ),
+)
+
+it.instance("applies env and file substitutions in tui.json", () =>
+  withCleanState(
+    withEnv(
+      "TUI_THEME_TEST",
+      "env-theme",
+      Effect.gen(function* () {
+        const fs = yield* AppFileSystem.Service
+        const test = yield* TestInstance
+        yield* fs.writeFileString(path.join(test.directory, "keybind.txt"), "ctrl+q")
+        yield* fs.writeJson(path.join(test.directory, "tui.json"), {
+          theme: "{env:TUI_THEME_TEST}",
+          keybinds: { app_exit: "{file:keybind.txt}" },
+        })
+
+        const config = yield* getTuiConfig(test.directory)
+        expect(config.theme).toBe("env-theme")
+        expect(config.keybinds.get("app.exit")?.[0]?.key).toBe("ctrl+q")
+      }),
+    ),
+  ),
+)
+
+it.instance("applies file substitutions when first identical token is in a commented line", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeFileString(path.join(test.directory, "theme.txt"), "resolved-theme")
+      yield* fs.writeFileString(
+        path.join(test.directory, "tui.jsonc"),
         `{
         `{
   // "theme": "{file:theme.txt}",
   // "theme": "{file:theme.txt}",
   "theme": "{file:theme.txt}"
   "theme": "{file:theme.txt}"
 }`,
 }`,
       )
       )
-    },
-  })
-  const config = await getTuiConfig(tmp.path)
-  expect(config.theme).toBe("resolved-theme")
-})
-
-test("loads .opencode/tui.json", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await fs.mkdir(path.join(dir, ".opencode"), { recursive: true })
-      await Bun.write(path.join(dir, ".opencode", "tui.json"), JSON.stringify({ diff_style: "stacked" }, null, 2))
-    },
-  })
-  const config = await getTuiConfig(tmp.path)
-  expect(config.diff_style).toBe("stacked")
-})
-
-test("supports tuple plugin specs with options in tui.json", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(
-        path.join(dir, "tui.json"),
-        JSON.stringify({
-          plugin: [["acme-plugin@1.2.3", { enabled: true, label: "demo" }]],
-        }),
-      )
-    },
-  })
-
-  const config = await getTuiConfig(tmp.path)
-  expect(config.plugin).toEqual([["acme-plugin@1.2.3", { enabled: true, label: "demo" }]])
-  expect(config.plugin_origins).toEqual([
-    {
-      spec: ["acme-plugin@1.2.3", { enabled: true, label: "demo" }],
-      scope: "local",
-      source: path.join(tmp.path, "tui.json"),
-    },
-  ])
-})
-
-test("deduplicates tuple plugin specs by name with higher precedence winning", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(
-        path.join(Global.Path.config, "tui.json"),
-        JSON.stringify({
-          plugin: [["acme-plugin@1.0.0", { source: "global" }]],
-        }),
-      )
-      await Bun.write(
-        path.join(dir, "tui.json"),
-        JSON.stringify({
-          plugin: [
-            ["acme-plugin@2.0.0", { source: "project" }],
-            ["second-plugin@3.0.0", { source: "project" }],
-          ],
-        }),
-      )
-    },
-  })
-
-  const config = await getTuiConfig(tmp.path)
-  expect(config.plugin).toEqual([
-    ["acme-plugin@2.0.0", { source: "project" }],
-    ["second-plugin@3.0.0", { source: "project" }],
-  ])
-  expect(config.plugin_origins).toEqual([
-    {
-      spec: ["acme-plugin@2.0.0", { source: "project" }],
-      scope: "local",
-      source: path.join(tmp.path, "tui.json"),
-    },
-    {
-      spec: ["second-plugin@3.0.0", { source: "project" }],
-      scope: "local",
-      source: path.join(tmp.path, "tui.json"),
-    },
-  ])
-})
-
-test("tracks global and local plugin metadata in merged tui config", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(
-        path.join(Global.Path.config, "tui.json"),
-        JSON.stringify({
-          plugin: ["global-plugin@1.0.0"],
-        }),
-      )
-      await Bun.write(
-        path.join(dir, "tui.json"),
-        JSON.stringify({
-          plugin: ["local-plugin@2.0.0"],
-        }),
-      )
-    },
-  })
 
 
-  const config = await getTuiConfig(tmp.path)
-  expect(config.plugin).toEqual(["global-plugin@1.0.0", "local-plugin@2.0.0"])
-  expect(config.plugin_origins).toEqual([
-    {
-      spec: "global-plugin@1.0.0",
-      scope: "global",
-      source: path.join(Global.Path.config, "tui.json"),
-    },
-    {
-      spec: "local-plugin@2.0.0",
-      scope: "local",
-      source: path.join(tmp.path, "tui.json"),
-    },
-  ])
-})
-
-test("merges plugin_enabled flags across config layers", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(
-        path.join(Global.Path.config, "tui.json"),
-        JSON.stringify({
-          plugin_enabled: {
-            "internal:sidebar-context": false,
-            "demo.plugin": true,
-          },
-        }),
-      )
-      await Bun.write(
-        path.join(dir, "tui.json"),
-        JSON.stringify({
-          plugin_enabled: {
-            "demo.plugin": false,
-            "local.plugin": true,
-          },
-        }),
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.theme).toBe("resolved-theme")
+    }),
+  ),
+)
+
+it.instance("loads .opencode/tui.json", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeWithDirs(
+        path.join(test.directory, ".opencode", "tui.json"),
+        JSON.stringify({ diff_style: "stacked" }, null, 2),
       )
       )
-    },
-  })
 
 
-  const config = await getTuiConfig(tmp.path)
-  expect(config.plugin_enabled).toEqual({
-    "internal:sidebar-context": false,
-    "demo.plugin": false,
-    "local.plugin": true,
-  })
-})
-
-test("silently skips malformed tui.json — load failures degrade to {}", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      await Bun.write(path.join(dir, "tui.json"), '{ "theme": "broken",')
-      await Bun.write(path.join(dir, ".opencode", "tui.json"), JSON.stringify({ theme: "fallback" }))
-    },
-  })
-
-  const config = await getTuiConfig(tmp.path)
-  // Project tui.json is malformed → silently skipped (logs a warning)
-  // .opencode/tui.json (lower precedence in this path) still loads
-  expect(config.theme).toBe("fallback")
-})
-
-test("silently skips non-ENOENT read failures (e.g. tui.json is a directory) — fallback layer still loads", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      // tui.json exists as a DIRECTORY rather than a file → readFileString fails
-      // with EISDIR (PlatformError reason ≠ NotFound). The fix in this PR routes
-      // that through catchCause → log + skip, so a fallback layer should still load.
-      await fs.mkdir(path.join(dir, "tui.json"), { recursive: true })
-      await Bun.write(path.join(dir, ".opencode", "tui.json"), JSON.stringify({ theme: "fallback" }))
-    },
-  })
-
-  const config = await getTuiConfig(tmp.path)
-  // Did NOT crash; .opencode/tui.json (lower precedence) still loads.
-  expect(config.theme).toBe("fallback")
-})
-
-test("missing tui.json — silently treated as empty (ENOENT path)", async () => {
-  await using tmp = await tmpdir({})
-
-  // No tui.json anywhere. Should not throw.
-  const config = await getTuiConfig(tmp.path)
-  expect(config).toBeDefined()
-  // No theme set anywhere.
-  expect(config.theme).toBeUndefined()
-})
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.diff_style).toBe("stacked")
+    }),
+  ),
+)
+
+it.instance("supports tuple plugin specs with options in tui.json", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeJson(path.join(test.directory, "tui.json"), {
+        plugin: [["acme-plugin@1.2.3", { enabled: true, label: "demo" }]],
+      })
+
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.plugin).toEqual([["acme-plugin@1.2.3", { enabled: true, label: "demo" }]])
+      expect(config.plugin_origins).toEqual([
+        {
+          spec: ["acme-plugin@1.2.3", { enabled: true, label: "demo" }],
+          scope: "local",
+          source: path.join(test.directory, "tui.json"),
+        },
+      ])
+    }),
+  ),
+)
+
+it.instance("deduplicates tuple plugin specs by name with higher precedence winning", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeJson(path.join(Global.Path.config, "tui.json"), {
+        plugin: [["acme-plugin@1.0.0", { source: "global" }]],
+      })
+      yield* fs.writeJson(path.join(test.directory, "tui.json"), {
+        plugin: [
+          ["acme-plugin@2.0.0", { source: "project" }],
+          ["second-plugin@3.0.0", { source: "project" }],
+        ],
+      })
+
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.plugin).toEqual([
+        ["acme-plugin@2.0.0", { source: "project" }],
+        ["second-plugin@3.0.0", { source: "project" }],
+      ])
+      expect(config.plugin_origins).toEqual([
+        {
+          spec: ["acme-plugin@2.0.0", { source: "project" }],
+          scope: "local",
+          source: path.join(test.directory, "tui.json"),
+        },
+        {
+          spec: ["second-plugin@3.0.0", { source: "project" }],
+          scope: "local",
+          source: path.join(test.directory, "tui.json"),
+        },
+      ])
+    }),
+  ),
+)
+
+it.instance("tracks global and local plugin metadata in merged tui config", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeJson(path.join(Global.Path.config, "tui.json"), { plugin: ["global-plugin@1.0.0"] })
+      yield* fs.writeJson(path.join(test.directory, "tui.json"), { plugin: ["local-plugin@2.0.0"] })
+
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.plugin).toEqual(["global-plugin@1.0.0", "local-plugin@2.0.0"])
+      expect(config.plugin_origins).toEqual([
+        {
+          spec: "global-plugin@1.0.0",
+          scope: "global",
+          source: path.join(Global.Path.config, "tui.json"),
+        },
+        {
+          spec: "local-plugin@2.0.0",
+          scope: "local",
+          source: path.join(test.directory, "tui.json"),
+        },
+      ])
+    }),
+  ),
+)
+
+it.instance("merges plugin_enabled flags across config layers", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeJson(path.join(Global.Path.config, "tui.json"), {
+        plugin_enabled: {
+          "internal:sidebar-context": false,
+          "demo.plugin": true,
+        },
+      })
+      yield* fs.writeJson(path.join(test.directory, "tui.json"), {
+        plugin_enabled: {
+          "demo.plugin": false,
+          "local.plugin": true,
+        },
+      })
+
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.plugin_enabled).toEqual({
+        "internal:sidebar-context": false,
+        "demo.plugin": false,
+        "local.plugin": true,
+      })
+    }),
+  ),
+)
+
+it.instance("silently skips malformed tui.json - load failures degrade to {}", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.writeFileString(path.join(test.directory, "tui.json"), '{ "theme": "broken",')
+      yield* fs.writeWithDirs(path.join(test.directory, ".opencode", "tui.json"), JSON.stringify({ theme: "fallback" }))
+
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.theme).toBe("fallback")
+    }),
+  ),
+)
+
+it.instance("silently skips non-ENOENT read failures (e.g. tui.json is a directory) - fallback layer still loads", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const fs = yield* AppFileSystem.Service
+      const test = yield* TestInstance
+      yield* fs.makeDirectory(path.join(test.directory, "tui.json"), { recursive: true })
+      yield* fs.writeWithDirs(path.join(test.directory, ".opencode", "tui.json"), JSON.stringify({ theme: "fallback" }))
+
+      const config = yield* getTuiConfig(test.directory)
+      expect(config.theme).toBe("fallback")
+    }),
+  ),
+)
+
+it.instance("missing tui.json - silently treated as empty (ENOENT path)", () =>
+  withCleanState(
+    Effect.gen(function* () {
+      const test = yield* TestInstance
+      const config = yield* getTuiConfig(test.directory)
+      expect(config).toBeDefined()
+      expect(config.theme).toBeUndefined()
+    }),
+  ),
+)