Skip to content

Commit adfa914

Browse files
author
bcode
committed
fix(plugin): drop scope-close deregistration of shutdown hooks
Fix 3 from the cubic review on PR #75 was a regression. The plugin-layer scope (under InstanceState) closes BEFORE the top-level finally in src/index.ts runs, via store.dispose(ctx) in effect-cmd's own finally. A scope-close finalizer empties pluginShutdownHooks before the host can iterate it, defeating the entire feature. Verified live: v0.1.8-rc1 in staging cloud V4 still produced orphan turn parents (trace 0ee0b31c-..., top_span_landed=False, total_cost=0, all session.llm children referencing one unlanded parent span). Multi-instance TUI mode bloat from accumulated closures is acceptable -- rare in practice, closures are tiny, and the hooks Set is intentionally module-level so it outlives Effect scopes.
1 parent 09a7178 commit adfa914

1 file changed

Lines changed: 9 additions & 11 deletions

File tree

packages/opencode/src/plugin/index.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -279,19 +279,17 @@ export const layer = Layer.effect(
279279
// OTel spans (e.g. bcode-laminar's turn span) — the bus-based
280280
// session.idle / server.instance.disposed paths race with scope
281281
// teardown and don't reliably deliver, so plugins need a direct sync
282-
// entry point. Deregister on instance disposal so multi-instance TUI
283-
// mode doesn't accumulate stale closures across reopens.
284-
const registered: Array<() => void> = []
282+
// entry point.
283+
//
284+
// Intentionally NOT deregistered on scope close: the plugin-layer
285+
// scope (under InstanceState) closes BEFORE the top-level finally in
286+
// src/index.ts runs (via store.dispose(ctx) in effect-cmd's own
287+
// finally). A scope-close finalizer would empty the Set before the
288+
// host could call it, defeating the entire feature. Multi-instance
289+
// TUI bloat from accumulated closures is acceptable.
285290
for (const hook of hooks) {
286-
if (!hook.shutdown) continue
287-
pluginShutdownHooks.add(hook.shutdown)
288-
registered.push(hook.shutdown)
291+
if (hook.shutdown) pluginShutdownHooks.add(hook.shutdown)
289292
}
290-
yield* Effect.addFinalizer(() =>
291-
Effect.sync(() => {
292-
for (const fn of registered) pluginShutdownHooks.delete(fn)
293-
}),
294-
)
295293

296294
return { hooks }
297295
}),

0 commit comments

Comments
 (0)