Skip to content

Commit ccebf08

Browse files
Apply PR #19139: effectify Config service
2 parents ceb6c05 + 8c0fae3 commit ccebf08

9 files changed

Lines changed: 492 additions & 373 deletions

File tree

packages/opencode/AGENTS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ See `specs/effect-migration.md` for the compact pattern reference and examples.
4949
- Prefer `Path.Path`, `Config`, `Clock`, and `DateTime` when those concerns are already inside Effect code.
5050
- For background loops or scheduled tasks, use `Effect.repeat` or `Effect.schedule` with `Effect.forkScoped` in the layer definition.
5151

52+
## Effect.cached for deduplication
53+
54+
Use `Effect.cached` when multiple concurrent callers should share a single in-flight computation rather than storing `Fiber | undefined` or `Promise | undefined` manually. See `specs/effect-migration.md` for the full pattern.
55+
5256
## Instance.bind — ALS for native callbacks
5357

5458
`Instance.bind(fn)` captures the current Instance AsyncLocalStorage context and restores it synchronously when called.

packages/opencode/specs/effect-migration.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,31 @@ yield *
121121

122122
The key insight: don't split init into a separate method with a `started` flag. Put everything in the `InstanceState.make` closure and let `ScopedCache` handle the run-once semantics.
123123

124+
## Effect.cached for deduplication
125+
126+
Use `Effect.cached` when multiple concurrent callers should share a single in-flight computation. It memoizes the result and deduplicates concurrent fibers — second caller joins the first caller's fiber instead of starting a new one.
127+
128+
```ts
129+
// Inside the layer — yield* to initialize the memo
130+
let cached = yield* Effect.cached(loadExpensive())
131+
132+
const get = Effect.fn("Foo.get")(function* () {
133+
return yield* cached // concurrent callers share the same fiber
134+
})
135+
136+
// To invalidate: swap in a fresh memo
137+
const invalidate = Effect.fn("Foo.invalidate")(function* () {
138+
cached = yield* Effect.cached(loadExpensive())
139+
})
140+
```
141+
142+
Prefer `Effect.cached` over these patterns:
143+
- Storing a `Fiber.Fiber | undefined` with manual check-and-fork (e.g. `file/index.ts` `ensure`)
144+
- Storing a `Promise<void>` task for deduplication (e.g. `skill/index.ts` `ensure`)
145+
- `let cached: X | undefined` with check-and-load (races when two callers see `undefined` before either resolves)
146+
147+
`Effect.cached` handles the run-once + concurrent-join semantics automatically. For invalidatable caches, reassign with `yield* Effect.cached(...)` — the old memo is discarded.
148+
124149
## Scheduled Tasks
125150

126151
For loops or periodic work, use `Effect.repeat` or `Effect.schedule` with `Effect.forkScoped` in the layer definition.
@@ -179,7 +204,7 @@ Still open and likely worth migrating:
179204
- [x] `Worktree`
180205
- [x] `Bus`
181206
- [x] `Command`
182-
- [ ] `Config`
207+
- [x] `Config`
183208
- [ ] `Session`
184209
- [ ] `SessionProcessor`
185210
- [ ] `SessionPrompt`

packages/opencode/src/cli/cmd/tui/worker.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,7 @@ export const rpc = {
137137
})
138138
},
139139
async reload() {
140-
Config.global.reset()
141-
await Instance.disposeAll()
140+
await Config.invalidate(true)
142141
},
143142
async setWorkspace(input: { workspaceID?: string }) {
144143
startEventStream({ directory: process.cwd(), workspaceID: input.workspaceID })

packages/opencode/src/cli/network.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export function withNetworkOptions<T>(yargs: Argv<T>) {
3737
}
3838

3939
export async function resolveNetworkOptions(args: NetworkOptions) {
40-
const config = await Config.global()
40+
const config = await Config.getGlobal()
4141
const portExplicitlySet = process.argv.includes("--port")
4242
const hostnameExplicitlySet = process.argv.includes("--hostname")
4343
const mdnsExplicitlySet = process.argv.includes("--mdns")

packages/opencode/src/cli/upgrade.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Flag } from "@/flag/flag"
44
import { Installation } from "@/installation"
55

66
export async function upgrade() {
7-
const config = await Config.global()
7+
const config = await Config.getGlobal()
88
const method = await Installation.method()
99
const latest = await Installation.latest(method).catch(() => {})
1010
if (!latest) return

0 commit comments

Comments
 (0)