Compare commits

...

3 Commits

Author SHA1 Message Date
Dax Raad 7188419f01 docs: document Effect v4 fork API changes and InstanceState init patterns
Document the proper pattern for making service init() non-blocking by
forking at the call site rather than inside the InstanceState.make closure.
This prevents state initialization issues when multiple methods access
shared state during startup.

Add clarification that Effect.fork and Effect.forkDaemon are removed in
Effect v4 beta, with Effect.forkIn(scope) as the replacement for forking
fibers into specific scopes.
2026-04-16 02:51:17 -04:00
Dax Raad 4fe1a09113 core: fix race condition in VCS state initialization by completing git operations synchronously during startup 2026-04-16 02:49:50 -04:00
Dax Raad 944562e0c9 perf: make vcs init non-blocking by forking git branch resolution 2026-04-16 02:44:43 -04:00
2 changed files with 9 additions and 2 deletions
+6
View File
@@ -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
+3 -2
View File
@@ -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)