Skip to content

fix(util): no filesystem side effects at global module load - #41619

Merged
kitlangton merged 4 commits into
v2from
fix/global-import-effects
Aug 11, 2026
Merged

fix(util): no filesystem side effects at global module load#41619
kitlangton merged 4 commits into
v2from
fix/global-import-effects

Conversation

@kitlangton

@kitlangton kitlangton commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

What

Importing @opencode-ai/util/global wrote to disk through three top-level awaits. This violates Effect layer acquisition discipline and prevents Cloudflare workerd startup.

How

  • Keep module scope and every static Global.Path property pure, with no top-level await or filesystem I/O.
  • Create data, config, state, log, bin, repos, and tmp directories during Global layer acquisition.
  • Canonicalize the default tmp path after acquisition; explicit layerWith tmp overrides retain precedence.
  • Make the file logger create its target parent before opening the file, since observability can initialize before Global acquisition.
  • Keep existing static Global.Path consumers unchanged; no non-test consumer reads Global.Path.tmp.

Testing

  • packages/util: bunx tsgo --noEmit
  • packages/util: bun test (fresh-process property-access purity, acquisition, canonicalization, and override precedence)
  • packages/core: bunx tsgo --noEmit
  • packages/core: bun test test/instruction-discovery.test.ts test/effect/observability.test.ts
  • packages/cli: bun test test/service.test.ts -t "managed service writes its registration once"
  • packages/server: bun test test/process.test.ts
  • Pre-push workspace typecheck: 33/33 tasks passed

@kitlangton
kitlangton force-pushed the fix/global-import-effects branch from ffb4b81 to a93c913 Compare August 10, 2026 23:43
@kitlangton

Copy link
Copy Markdown
Contributor Author

Confirmed and fixed the remaining regressions. Local logging was acquired before the Global layer, so Logger.toFile attempted to open Global.Path.log before its parent existed; managed-service subprocesses exited at the same point before publishing registration. fileLogger now creates the target parent directory immediately before opening the file, without restoring import-time I/O.

Also replaced InstructionDiscovery's unwritable /global fixture path now that layerWith acquires its configured directories. Focused results: util 6/6, InstructionDiscovery + observability 13/13, and the previously timing-out managed service writes its registration once passes. Full service run passed all CI-reported lifecycle cases; the separate a failed service stays registered... timeout reproduces unchanged on origin/v2 when run alone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant