From 5f5b1f461697dcf236ae8746b1b8170f96ee05c1 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Fri, 10 Jul 2026 13:19:21 -0400 Subject: [PATCH 1/3] fix(core): defer snapshot repository discovery --- packages/core/src/snapshot.ts | 42 +++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/packages/core/src/snapshot.ts b/packages/core/src/snapshot.ts index 8ef532f488d4..4c5a25a3d8db 100644 --- a/packages/core/src/snapshot.ts +++ b/packages/core/src/snapshot.ts @@ -91,32 +91,41 @@ const layer = Layer.effect( const git = yield* Git.Service const global = yield* Global.Service const location = yield* Location.Service - const source = yield* git.repo.discover(location.project.directory) - const worktree = source - ? AbsolutePath.make(yield* fs.realPath(source.worktree).pipe(Effect.orDie)) - : location.project.directory - const gitDirectory = AbsolutePath.make(path.join(global.data, "snapshot", location.project.id, Hash.fast(worktree))) + const repositoryState = yield* Effect.cached( + Effect.gen(function* () { + const source = yield* git.repo.discover(location.project.directory) + const worktree = source + ? AbsolutePath.make(yield* fs.realPath(source.worktree).pipe(Effect.orDie)) + : location.project.directory + return { + source, + worktree, + gitDirectory: AbsolutePath.make(path.join(global.data, "snapshot", location.project.id, Hash.fast(worktree))), + } + }).pipe(Effect.uninterruptible), + ) const scope = Effect.fnUntraced(function* () { - const relative = path.relative(worktree, location.directory) + const relative = path.relative((yield* repositoryState).worktree, location.directory) if (relative.startsWith("..") || path.isAbsolute(relative)) return yield* new Error({ operation: "capture", message: "Location is outside the project" }) return RelativePath.make(relative.replaceAll("\\", "/") || ".") }) const repository = Effect.fnUntraced(function* () { - if (!source) return yield* new Error({ operation: "capture", message: "Project is not a Git repository" }) - if (yield* fs.existsSafe(path.join(gitDirectory, "HEAD"))) + const current = yield* repositoryState + if (!current.source) return yield* new Error({ operation: "capture", message: "Project is not a Git repository" }) + if (yield* fs.existsSafe(path.join(current.gitDirectory, "HEAD"))) return new Git.Repository({ - worktree, - gitDirectory, - commonDirectory: gitDirectory, + worktree: current.worktree, + gitDirectory: current.gitDirectory, + commonDirectory: current.gitDirectory, }) return yield* git.repo .create({ - worktree, - gitDirectory, - seed: source, + worktree: current.worktree, + gitDirectory: current.gitDirectory, + seed: current.source, }) .pipe(Effect.mapError((cause) => failure("capture", cause))) }) @@ -130,6 +139,7 @@ const layer = Layer.effect( if (!(yield* enabled())) return undefined return yield* Effect.gen(function* () { const repo = yield* repository() + const source = (yield* repositoryState).source return ID.make( yield* git.tree.capture({ repository: repo, @@ -151,6 +161,7 @@ const layer = Layer.effect( const files = Effect.fn("Snapshot.files")(function* (input: CompareInput) { const comparison = yield* compare("files", input) const files = yield* git.tree.files(comparison).pipe(Effect.mapError((cause) => failure("files", cause))) + const source = (yield* repositoryState).source if (!source) return files const ignored = yield* git.index .ignored({ repository: source, paths: files }) @@ -161,6 +172,7 @@ const layer = Layer.effect( const diff = Effect.fn("Snapshot.diff")(function* (input: DiffInput) { const comparison = yield* compare("diff", input) const files = yield* git.tree.files(comparison).pipe(Effect.mapError((cause) => failure("diff", cause))) + const source = (yield* repositoryState).source const ignored = source ? yield* git.index .ignored({ repository: source, paths: files }) @@ -177,6 +189,7 @@ const layer = Layer.effect( const plan = Effect.fnUntraced(function* (operation: "preview" | "restore", input: RestoreInput) { const files = new Map() + const worktree = (yield* repositoryState).worktree for (const [file, snapshot] of input.files) { const absolute = path.resolve(worktree, file) if (!FSUtil.contains(worktree, absolute)) @@ -189,6 +202,7 @@ const layer = Layer.effect( const preview = Effect.fn("Snapshot.preview")(function* (input: PreviewInput) { if (!(yield* enabled())) return yield* new Error({ operation: "preview", message: "Snapshots are disabled" }) const repo = yield* repository().pipe(Effect.mapError((cause) => failure("preview", cause))) + const source = (yield* repositoryState).source const files = yield* plan("preview", input) const current = yield* git.tree .capture({ From adfdc26c5d73cbee23d21b69f1946d7e9514c58c Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Fri, 10 Jul 2026 17:13:10 -0400 Subject: [PATCH 2/3] fix(core): preserve lazy snapshot initialization --- packages/core/src/snapshot.ts | 85 +++++++++++++++-------------- packages/core/test/snapshot.test.ts | 69 ++++++++++++++++++++++- 2 files changed, 112 insertions(+), 42 deletions(-) diff --git a/packages/core/src/snapshot.ts b/packages/core/src/snapshot.ts index 4c5a25a3d8db..60838ee5ab2d 100644 --- a/packages/core/src/snapshot.ts +++ b/packages/core/src/snapshot.ts @@ -2,7 +2,7 @@ export * as Snapshot from "./snapshot" import { makeLocationNode } from "./effect/app-node" import path from "path" -import { Context, Effect, Layer, Schema } from "effect" +import { Context, Effect, Fiber, Layer, Schema, Scope } from "effect" import { Config } from "./config" import { File } from "./file" import { FSUtil } from "./fs-util" @@ -91,7 +91,9 @@ const layer = Layer.effect( const git = yield* Git.Service const global = yield* Global.Service const location = yield* Location.Service - const repositoryState = yield* Effect.cached( + const lifetime = yield* Scope.Scope + // Cache a scope-owned fiber so caller cancellation stops waiting without poisoning shared initialization. + const repositoryStateFiber = yield* Effect.cached( Effect.gen(function* () { const source = yield* git.repo.discover(location.project.directory) const worktree = source @@ -102,11 +104,12 @@ const layer = Layer.effect( worktree, gitDirectory: AbsolutePath.make(path.join(global.data, "snapshot", location.project.id, Hash.fast(worktree))), } - }).pipe(Effect.uninterruptible), + }).pipe(Effect.forkIn(lifetime)), ) + const repositoryState = repositoryStateFiber.pipe(Effect.uninterruptible, Effect.flatMap(Fiber.join)) - const scope = Effect.fnUntraced(function* () { - const relative = path.relative((yield* repositoryState).worktree, location.directory) + const scope = Effect.fnUntraced(function* (worktree: AbsolutePath) { + const relative = path.relative(worktree, location.directory) if (relative.startsWith("..") || path.isAbsolute(relative)) return yield* new Error({ operation: "capture", message: "Location is outside the project" }) return RelativePath.make(relative.replaceAll("\\", "/") || ".") @@ -116,18 +119,19 @@ const layer = Layer.effect( const current = yield* repositoryState if (!current.source) return yield* new Error({ operation: "capture", message: "Project is not a Git repository" }) if (yield* fs.existsSafe(path.join(current.gitDirectory, "HEAD"))) - return new Git.Repository({ - worktree: current.worktree, - gitDirectory: current.gitDirectory, - commonDirectory: current.gitDirectory, - }) - return yield* git.repo - .create({ + return { + source: current.source, worktree: current.worktree, - gitDirectory: current.gitDirectory, - seed: current.source, - }) + snapshot: new Git.Repository({ + worktree: current.worktree, + gitDirectory: current.gitDirectory, + commonDirectory: current.gitDirectory, + }), + } + const snapshot = yield* git.repo + .create({ worktree: current.worktree, gitDirectory: current.gitDirectory, seed: current.source }) .pipe(Effect.mapError((cause) => failure("capture", cause))) + return { source: current.source, worktree: current.worktree, snapshot } }) const enabled = Effect.fnUntraced(function* () { @@ -139,12 +143,11 @@ const layer = Layer.effect( if (!(yield* enabled())) return undefined return yield* Effect.gen(function* () { const repo = yield* repository() - const source = (yield* repositoryState).source return ID.make( yield* git.tree.capture({ - repository: repo, - scopes: [yield* scope()], - ignores: source, + repository: repo.snapshot, + scopes: [yield* scope(repo.worktree)], + ignores: repo.source, maximumUntrackedFileBytes: 2 * 1024 * 1024, }), ) @@ -155,41 +158,42 @@ const layer = Layer.effect( const compare = Effect.fnUntraced(function* (operation: "files" | "diff", input: CompareInput) { const repo = yield* repository().pipe(Effect.mapError((cause) => failure(operation, cause))) - return { repository: repo, from: Git.TreeID.make(input.from), to: Git.TreeID.make(input.to) } + return { + source: repo.source, + input: { repository: repo.snapshot, from: Git.TreeID.make(input.from), to: Git.TreeID.make(input.to) }, + } }) const files = Effect.fn("Snapshot.files")(function* (input: CompareInput) { const comparison = yield* compare("files", input) - const files = yield* git.tree.files(comparison).pipe(Effect.mapError((cause) => failure("files", cause))) - const source = (yield* repositoryState).source - if (!source) return files + const files = yield* git.tree.files(comparison.input).pipe(Effect.mapError((cause) => failure("files", cause))) const ignored = yield* git.index - .ignored({ repository: source, paths: files }) + .ignored({ repository: comparison.source, paths: files }) .pipe(Effect.mapError((cause) => failure("files", cause))) return files.filter((file) => !ignored.has(file)) }) const diff = Effect.fn("Snapshot.diff")(function* (input: DiffInput) { const comparison = yield* compare("diff", input) - const files = yield* git.tree.files(comparison).pipe(Effect.mapError((cause) => failure("diff", cause))) - const source = (yield* repositoryState).source - const ignored = source - ? yield* git.index - .ignored({ repository: source, paths: files }) - .pipe(Effect.mapError((cause) => failure("diff", cause))) - : new Set() + const files = yield* git.tree.files(comparison.input).pipe(Effect.mapError((cause) => failure("diff", cause))) + const ignored = yield* git.index + .ignored({ repository: comparison.source, paths: files }) + .pipe(Effect.mapError((cause) => failure("diff", cause))) return yield* git.tree .diff({ - ...comparison, + ...comparison.input, context: input.context, paths: (input.paths ?? files).filter((file) => !ignored.has(file)), }) .pipe(Effect.mapError((cause) => failure("diff", cause))) }) - const plan = Effect.fnUntraced(function* (operation: "preview" | "restore", input: RestoreInput) { + const plan = Effect.fnUntraced(function* ( + operation: "preview" | "restore", + worktree: AbsolutePath, + input: RestoreInput, + ) { const files = new Map() - const worktree = (yield* repositoryState).worktree for (const [file, snapshot] of input.files) { const absolute = path.resolve(worktree, file) if (!FSUtil.contains(worktree, absolute)) @@ -202,19 +206,18 @@ const layer = Layer.effect( const preview = Effect.fn("Snapshot.preview")(function* (input: PreviewInput) { if (!(yield* enabled())) return yield* new Error({ operation: "preview", message: "Snapshots are disabled" }) const repo = yield* repository().pipe(Effect.mapError((cause) => failure("preview", cause))) - const source = (yield* repositoryState).source - const files = yield* plan("preview", input) + const files = yield* plan("preview", repo.worktree, input) const current = yield* git.tree .capture({ - repository: repo, + repository: repo.snapshot, scopes: Array.from(files.keys()), - ignores: source, + ignores: repo.source, maximumUntrackedFileBytes: 2 * 1024 * 1024, }) .pipe(Effect.mapError((cause) => failure("preview", cause))) return yield* git.tree .preview({ - repository: repo, + repository: repo.snapshot, current, files, context: input.context, @@ -226,14 +229,14 @@ const layer = Layer.effect( if (!(yield* enabled())) return yield* new Error({ operation: "restore", message: "Snapshots are disabled" }) const repo = yield* repository().pipe(Effect.mapError((cause) => failure("restore", cause))) yield* git.tree - .restore({ repository: repo, files: yield* plan("restore", input) }) + .restore({ repository: repo.snapshot, files: yield* plan("restore", repo.worktree, input) }) .pipe(Effect.mapError((cause) => failure("restore", cause))) }) const checkout = Effect.fn("Snapshot.checkout")(function* (snapshot: ID) { const repo = yield* repository().pipe(Effect.mapError((cause) => failure("restore", cause))) yield* git.tree - .checkout({ repository: repo, tree: Git.TreeID.make(snapshot) }) + .checkout({ repository: repo.snapshot, tree: Git.TreeID.make(snapshot) }) .pipe(Effect.mapError((cause) => failure("restore", cause))) }) diff --git a/packages/core/test/snapshot.test.ts b/packages/core/test/snapshot.test.ts index 3bc0f341013e..5f7ec0a6000d 100644 --- a/packages/core/test/snapshot.test.ts +++ b/packages/core/test/snapshot.test.ts @@ -2,8 +2,9 @@ import { $ } from "bun" import { describe, expect } from "bun:test" import fs from "fs/promises" import path from "path" -import { Effect, Layer } from "effect" +import { Deferred, Effect, Fiber, Layer } from "effect" import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" +import { Git } from "@opencode-ai/core/git" import { Global } from "@opencode-ai/core/global" import { Location } from "@opencode-ai/core/location" import { AbsolutePath, RelativePath } from "@opencode-ai/core/schema" @@ -13,6 +14,72 @@ import { tmpdir } from "./fixture/tmpdir" import { testEffect } from "./lib/effect" describe("Snapshot", () => { + testEffect(Layer.empty).live("keeps lazy repository discovery after the first caller is interrupted", () => + Effect.acquireUseRelease( + Effect.promise(() => tmpdir()), + (tmp) => + Effect.gen(function* () { + const project = path.join(tmp.path, "project") + yield* Effect.promise(async () => { + await fs.mkdir(project) + await fs.writeFile(path.join(project, "tracked.txt"), "one\n") + await $`git init`.cwd(project).quiet() + await $`git config core.fsmonitor false`.cwd(project).quiet() + await $`git config commit.gpgsign false`.cwd(project).quiet() + await $`git config user.email test@opencode.test`.cwd(project).quiet() + await $`git config user.name Test`.cwd(project).quiet() + await $`git add .`.cwd(project).quiet() + await $`git commit -m initial`.cwd(project).quiet() + }) + + const git = yield* Git.Service.pipe(Effect.provide(AppNodeBuilder.build(Git.node))) + const location = yield* Location.Service.pipe( + Effect.provide( + AppNodeBuilder.build(Location.boundNode(Location.Ref.make({ directory: AbsolutePath.make(project) }))), + ), + ) + const started = yield* Deferred.make() + const release = yield* Deferred.make() + let discoveries = 0 + const instrumented = Git.Service.of({ + ...git, + repo: { + ...git.repo, + discover: (input) => + Effect.gen(function* () { + discoveries++ + yield* Deferred.succeed(started, undefined) + yield* Deferred.await(release) + return yield* git.repo.discover(input) + }), + }, + }) + const layer = AppNodeBuilder.build(Snapshot.node, [ + [Location.node, Layer.succeed(Location.Service, location)], + [Global.node, Global.layerWith({ data: tmp.path, config: path.join(tmp.path, "config") })], + [Git.node, Layer.succeed(Git.Service, instrumented)], + ]) + + yield* Effect.gen(function* () { + const snapshot = yield* Snapshot.Service + expect(discoveries).toBe(0) + + const interrupted = yield* snapshot.capture().pipe(Effect.forkChild) + yield* Deferred.await(started) + expect(discoveries).toBe(1) + yield* Fiber.interrupt(interrupted) + + const capture = yield* snapshot.capture().pipe(Effect.forkChild) + expect(discoveries).toBe(1) + yield* Deferred.succeed(release, undefined) + expect(yield* Fiber.join(capture)).toBeDefined() + expect(discoveries).toBe(1) + }).pipe(Effect.provide(layer)) + }), + (tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()), + ), + ) + testEffect(Layer.empty).live("captures and restores Location-scoped changes", () => Effect.acquireUseRelease( Effect.promise(() => tmpdir()), From 996f6fae72c798ffd6779d589095f61f5a2c1394 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Fri, 10 Jul 2026 17:26:02 -0400 Subject: [PATCH 3/3] refactor(core): share snapshot repository initialization --- packages/core/src/snapshot.ts | 68 ++++++++++++----------------- packages/core/test/snapshot.test.ts | 14 ++++-- 2 files changed, 39 insertions(+), 43 deletions(-) diff --git a/packages/core/src/snapshot.ts b/packages/core/src/snapshot.ts index 60838ee5ab2d..41072e3ce3e0 100644 --- a/packages/core/src/snapshot.ts +++ b/packages/core/src/snapshot.ts @@ -93,20 +93,23 @@ const layer = Layer.effect( const location = yield* Location.Service const lifetime = yield* Scope.Scope // Cache a scope-owned fiber so caller cancellation stops waiting without poisoning shared initialization. - const repositoryStateFiber = yield* Effect.cached( + const repositoryFiber = yield* Effect.cached( Effect.gen(function* () { const source = yield* git.repo.discover(location.project.directory) - const worktree = source - ? AbsolutePath.make(yield* fs.realPath(source.worktree).pipe(Effect.orDie)) - : location.project.directory - return { - source, - worktree, - gitDirectory: AbsolutePath.make(path.join(global.data, "snapshot", location.project.id, Hash.fast(worktree))), - } + if (!source) return yield* new Error({ operation: "capture", message: "Project is not a Git repository" }) + const worktree = AbsolutePath.make(yield* fs.realPath(source.worktree).pipe(Effect.orDie)) + const gitDirectory = AbsolutePath.make( + path.join(global.data, "snapshot", location.project.id, Hash.fast(worktree)), + ) + const snapshotRepository = (yield* fs.existsSafe(path.join(gitDirectory, "HEAD"))) + ? new Git.Repository({ worktree, gitDirectory, commonDirectory: gitDirectory }) + : yield* git.repo + .create({ worktree, gitDirectory, seed: source }) + .pipe(Effect.mapError((cause) => failure("capture", cause))) + return { source, worktree, snapshotRepository } }).pipe(Effect.forkIn(lifetime)), ) - const repositoryState = repositoryStateFiber.pipe(Effect.uninterruptible, Effect.flatMap(Fiber.join)) + const repository = repositoryFiber.pipe(Effect.uninterruptible, Effect.flatMap(Fiber.join)) const scope = Effect.fnUntraced(function* (worktree: AbsolutePath) { const relative = path.relative(worktree, location.directory) @@ -115,25 +118,6 @@ const layer = Layer.effect( return RelativePath.make(relative.replaceAll("\\", "/") || ".") }) - const repository = Effect.fnUntraced(function* () { - const current = yield* repositoryState - if (!current.source) return yield* new Error({ operation: "capture", message: "Project is not a Git repository" }) - if (yield* fs.existsSafe(path.join(current.gitDirectory, "HEAD"))) - return { - source: current.source, - worktree: current.worktree, - snapshot: new Git.Repository({ - worktree: current.worktree, - gitDirectory: current.gitDirectory, - commonDirectory: current.gitDirectory, - }), - } - const snapshot = yield* git.repo - .create({ worktree: current.worktree, gitDirectory: current.gitDirectory, seed: current.source }) - .pipe(Effect.mapError((cause) => failure("capture", cause))) - return { source: current.source, worktree: current.worktree, snapshot } - }) - const enabled = Effect.fnUntraced(function* () { if (location.vcs?.type !== "git") return false return Config.latest(yield* config.entries(), "snapshots") !== false @@ -142,10 +126,10 @@ const layer = Layer.effect( const capture = Effect.fn("Snapshot.capture")(function* () { if (!(yield* enabled())) return undefined return yield* Effect.gen(function* () { - const repo = yield* repository() + const repo = yield* repository return ID.make( yield* git.tree.capture({ - repository: repo.snapshot, + repository: repo.snapshotRepository, scopes: [yield* scope(repo.worktree)], ignores: repo.source, maximumUntrackedFileBytes: 2 * 1024 * 1024, @@ -157,10 +141,14 @@ const layer = Layer.effect( }) const compare = Effect.fnUntraced(function* (operation: "files" | "diff", input: CompareInput) { - const repo = yield* repository().pipe(Effect.mapError((cause) => failure(operation, cause))) + const repo = yield* repository.pipe(Effect.mapError((cause) => failure(operation, cause))) return { source: repo.source, - input: { repository: repo.snapshot, from: Git.TreeID.make(input.from), to: Git.TreeID.make(input.to) }, + input: { + repository: repo.snapshotRepository, + from: Git.TreeID.make(input.from), + to: Git.TreeID.make(input.to), + }, } }) @@ -205,11 +193,11 @@ const layer = Layer.effect( const preview = Effect.fn("Snapshot.preview")(function* (input: PreviewInput) { if (!(yield* enabled())) return yield* new Error({ operation: "preview", message: "Snapshots are disabled" }) - const repo = yield* repository().pipe(Effect.mapError((cause) => failure("preview", cause))) + const repo = yield* repository.pipe(Effect.mapError((cause) => failure("preview", cause))) const files = yield* plan("preview", repo.worktree, input) const current = yield* git.tree .capture({ - repository: repo.snapshot, + repository: repo.snapshotRepository, scopes: Array.from(files.keys()), ignores: repo.source, maximumUntrackedFileBytes: 2 * 1024 * 1024, @@ -217,7 +205,7 @@ const layer = Layer.effect( .pipe(Effect.mapError((cause) => failure("preview", cause))) return yield* git.tree .preview({ - repository: repo.snapshot, + repository: repo.snapshotRepository, current, files, context: input.context, @@ -227,16 +215,16 @@ const layer = Layer.effect( const restore = Effect.fn("Snapshot.restore")(function* (input: RestoreInput) { if (!(yield* enabled())) return yield* new Error({ operation: "restore", message: "Snapshots are disabled" }) - const repo = yield* repository().pipe(Effect.mapError((cause) => failure("restore", cause))) + const repo = yield* repository.pipe(Effect.mapError((cause) => failure("restore", cause))) yield* git.tree - .restore({ repository: repo.snapshot, files: yield* plan("restore", repo.worktree, input) }) + .restore({ repository: repo.snapshotRepository, files: yield* plan("restore", repo.worktree, input) }) .pipe(Effect.mapError((cause) => failure("restore", cause))) }) const checkout = Effect.fn("Snapshot.checkout")(function* (snapshot: ID) { - const repo = yield* repository().pipe(Effect.mapError((cause) => failure("restore", cause))) + const repo = yield* repository.pipe(Effect.mapError((cause) => failure("restore", cause))) yield* git.tree - .checkout({ repository: repo.snapshot, tree: Git.TreeID.make(snapshot) }) + .checkout({ repository: repo.snapshotRepository, tree: Git.TreeID.make(snapshot) }) .pipe(Effect.mapError((cause) => failure("restore", cause))) }) diff --git a/packages/core/test/snapshot.test.ts b/packages/core/test/snapshot.test.ts index 5f7ec0a6000d..19966e9d29a7 100644 --- a/packages/core/test/snapshot.test.ts +++ b/packages/core/test/snapshot.test.ts @@ -41,16 +41,21 @@ describe("Snapshot", () => { const started = yield* Deferred.make() const release = yield* Deferred.make() let discoveries = 0 + let creations = 0 const instrumented = Git.Service.of({ ...git, repo: { ...git.repo, - discover: (input) => + discover: (input) => { + discoveries++ + return git.repo.discover(input) + }, + create: (input) => Effect.gen(function* () { - discoveries++ + creations++ yield* Deferred.succeed(started, undefined) yield* Deferred.await(release) - return yield* git.repo.discover(input) + return yield* git.repo.create(input) }), }, }) @@ -67,13 +72,16 @@ describe("Snapshot", () => { const interrupted = yield* snapshot.capture().pipe(Effect.forkChild) yield* Deferred.await(started) expect(discoveries).toBe(1) + expect(creations).toBe(1) yield* Fiber.interrupt(interrupted) const capture = yield* snapshot.capture().pipe(Effect.forkChild) expect(discoveries).toBe(1) + expect(creations).toBe(1) yield* Deferred.succeed(release, undefined) expect(yield* Fiber.join(capture)).toBeDefined() expect(discoveries).toBe(1) + expect(creations).toBe(1) }).pipe(Effect.provide(layer)) }), (tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),