summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDax <[email protected]>2026-04-16 03:02:19 -0400
committerGitHub <[email protected]>2026-04-16 03:02:19 -0400
commit3f7df08be97a0a6f6d224c1c8c93ace80ad97c33 (patch)
tree2b9ccda9e4c50f31517d9a7844790ec05201c6a4
parentef6c26c730c7767ec5fc904927f2c5f1628de190 (diff)
downloadopencode-3f7df08be97a0a6f6d224c1c8c93ace80ad97c33.tar.gz
opencode-3f7df08be97a0a6f6d224c1c8c93ace80ad97c33.zip
perf: make vcs init non-blocking by forking git branch resolution (#22771)
-rw-r--r--packages/opencode/AGENTS.md6
-rw-r--r--packages/opencode/src/project/vcs.ts5
2 files changed, 9 insertions, 2 deletions
diff --git a/packages/opencode/AGENTS.md b/packages/opencode/AGENTS.md
index fd9e597e7..f0f32fdd1 100644
--- a/packages/opencode/AGENTS.md
+++ b/packages/opencode/AGENTS.md
@@ -39,6 +39,12 @@ See `specs/effect/migration.md` for the compact pattern reference and examples.
- Do the work directly in the `InstanceState.make` closure — `ScopedCache` handles run-once semantics. Don't add fibers, `ensure()` callbacks, or `started` flags on top.
- Use `Effect.addFinalizer` or `Effect.acquireRelease` inside the `InstanceState.make` closure for cleanup (subscriptions, process teardown, etc.).
- Use `Effect.forkScoped` inside the closure for background stream consumers — the fiber is interrupted when the instance is disposed.
+- To make a service's `init()` non-blocking, fork `InstanceState.get(state)` at the `init()` call site (e.g. `Effect.forkIn(scope)`), not by forking work inside the `InstanceState.make` closure. Forking inside the closure leaves state incomplete for other methods that read it.
+- `src/project/bootstrap.ts` already wraps every service `init()` in `Effect.forkDetach`, so `init()` is fire-and-forget in production. Keep `init()` methods synchronous internally; the caller controls concurrency.
+
+## Effect v4 beta API
+
+- `Effect.fork` and `Effect.forkDaemon` do not exist. Use `Effect.forkIn(scope)` to fork a fiber into a specific scope.
## Preferred Effect services
diff --git a/packages/opencode/src/project/vcs.ts b/packages/opencode/src/project/vcs.ts
index 559371859..b1375a7b7 100644
--- a/packages/opencode/src/project/vcs.ts
+++ b/packages/opencode/src/project/vcs.ts
@@ -1,4 +1,4 @@
-import { Effect, Layer, Context, Stream } from "effect"
+import { Effect, Layer, Context, Stream, Scope } from "effect"
import { formatPatch, structuredPatch } from "diff"
import path from "path"
import { Bus } from "@/bus"
@@ -157,6 +157,7 @@ export const layer: Layer.Layer<Service, never, AppFileSystem.Service | Git.Serv
const fs = yield* AppFileSystem.Service
const git = yield* Git.Service
const bus = yield* Bus.Service
+ const scope = yield* Scope.Scope
const state = yield* InstanceState.make<State>(
Effect.fn("Vcs.state")(function* (ctx) {
@@ -194,7 +195,7 @@ export const layer: Layer.Layer<Service, never, AppFileSystem.Service | Git.Serv
return Service.of({
init: Effect.fn("Vcs.init")(function* () {
- yield* InstanceState.get(state)
+ yield* InstanceState.get(state).pipe(Effect.forkIn(scope))
}),
branch: Effect.fn("Vcs.branch")(function* () {
return yield* InstanceState.use(state, (x) => x.current)