Skip to content

refactor(core): resolve database and websearch config through Effect Config - #34935

Closed
kitlangton wants to merge 1 commit into
v2from
config-effect-v2
Closed

kitlangton wants to merge 1 commit into
v2from
config-effect-v2

Conversation

@kitlangton

Copy link
Copy Markdown
Contributor

Consolidates #34846 and #34920 onto v2 (they were mistakenly opened against dev). Moves core runtime config off import-time process.env snapshots and onto Effect Config, with config ownership distributed to the services that consume it, plus a ConfigProvider seam for embedded hosts. ConfigProvider becomes the single test seam.

What changed

Database (packages/core/src/database/database.ts)

  • Placement was baked at module import (layerFromPath(path()) evaluated on import, reading the Flag env snapshot). There is now exactly one public placement value: Database.configuredPath, an Effect that resolves OPENCODE_DB / OPENCODE_DISABLE_CHANNEL_DB through Effect Config. The node layer is Layer.unwrap(Effect.map(configuredPath, layerFromPath)), built per runtime instead of per import.
  • Database.path() is deleted rather than kept as a V1 compat shim. Its V1 consumers were already Effect-shaped and now yield configuredPath directly: opencode db / opencode db path and the resetDatabase test fixture. This removes a real skew surface — previously path() read the live environment while the layer read the frozen first-use snapshot, so late env mutation could make opencode db path disagree with the running app.

Flag (packages/core/src/flag/flag.ts)

  • One truthiness grammar, two readers: truthy(name) reads the environment at call time, truthyConfig(name) reads the same grammar through Effect Config so layer builds can be steered by a ConfigProvider.
  • OPENCODE_DB is removed from the Flag snapshot; database config now lives in the database module.

WebSearch (packages/core/src/tool/websearch.ts)

  • defaultConfigLayer decodes one Config.all({...}) when the layer is built, instead of reading process.env at module scope.
  • Parsing deliberately keeps the legacy lenient grammar via truthyConfig (case-insensitive true/1, missing or malformed → false). This layer builds at Location boot, where a strict-decode defect would surface as opaque session failures; OPENCODE_EXPERIMENTAL=TRUE keeps meaning enabled. An invalid OPENCODE_WEBSEARCH_PROVIDER logs a warning and is ignored, matching old behavior.

Embedded host seam (packages/sdk-next/src/opencode.ts)

  • The default ConfigProvider.fromEnv() snapshots the process environment on first use, so runtime env mutation cannot steer later layer builds. OpenCode.create(options?) now accepts a configProvider, provided to both the application-node build and the embedded routes layer, so per-host config (like database placement) resolves against it. OpenCode.layerWith(options) mirrors it for the layer form.
  • Scope is documented precisely: the seam covers the host's process-global layer builds; Location-scoped layers build lazily outside host construction and still read the process default. Replacement semantics are deliberate — compose ConfigProvider.orElse(ConfigProvider.fromEnv()) for env fallback.

Tests

  • New packages/core/test/database-path.test.ts: exercises configuredPath through ConfigProvider.fromUnknown (:memory:, absolute, relative, channel-default) and proves the layer seam — the preload pins OPENCODE_DB=":memory:" in the environment, and a provider override wins at layer build.
  • tool-websearch.test.ts: decoding tests driven entirely by ConfigProvider.fromUnknown, including lenient-grammar regressions (TRUE enables, banana disables, invalid provider ignored).
  • sdk-next/test/embedded.test.ts: Flag.OPENCODE_DB = ":memory:" becomes process.env.OPENCODE_DB = ":memory:" (the Flag entry no longer exists; the env read happens at layer build, after module scope). New test proves a host configProvider places the database at a per-host path while the process env says :memory:.
  • httpapi-exercise/environment.ts: drops the redundant Flag.OPENCODE_DB mutation and documents the must-run-before-first-Config-read ordering contract.

Design notes

  • Deliberately not a central RuntimeConfig/Flag service: config values are inputs owned by their consuming services, read once at layer build. Flag shrinks by attrition as remaining keys migrate (OPENCODE_MODELS_*, OTLP, etc. are follow-ups).
  • Lenient vs strict parsing was reviewed explicitly (three-agent simplify/safety pass): strict Config.boolean would have turned 7 previously-tolerated env values (including uppercase TRUE) into startup/Location-boot defects. Strictness may be worth revisiting per-variable, but as an explicit decision — not a side effect of the migration.
  • Config.schema(...) was evaluated and skipped for these call sites: it fits when reusing a domain schema with strict failure semantics, but both call sites need the lenient legacy grammar, and the provider field needs warn-and-ignore, which Config.option can't express (it only swallows missing data). Future migrations like OPENCODE_MODELS_URL are better candidates.
  • Behavior invariant: two env-driven builds in one process always resolve the same database path, because the default provider's env snapshot is cached process-wide. Only an explicit host ConfigProvider can differ, which is the feature.
  • Considered hoisting V1's ConfigService helper (packages/opencode/src/effect/config-service.ts) into core; skipped since both call sites have a natural home. Worth revisiting once more config-backed services exist in core (could also unify ServerAuth.Config and V1 RuntimeFlags).

Verification (against v2)

  • bun typecheck in packages/core, packages/sdk-next, packages/opencode
  • packages/core suite: 1101 pass; the 6 failures (watcher timeouts under full-suite load) reproduce identically on clean origin/v2 locally and pass in isolation
  • packages/sdk-next embedded suite: 5 pass / 0 fail (includes the new per-host placement test)
  • packages/opencode script/httpapi-exercise.ts --mode coverage: 209 pass / 0 fail (missing=15 routes without scenarios is pre-existing on v2)
  • oxlint + Prettier on changed files

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Automated PR Cleanup

Thank you for contributing to opencode.

Due to the high volume of PRs from users and AI agents, we periodically close older PRs using automated criteria so maintainers can focus review time on the most active and community-supported contributions.

This PR was closed because it matched the following cleanup criteria:

  • The PR was created more than 1 month ago
  • The PR had fewer than 2 positive reactions
  • Positive reactions are counted as thumbs-up, heart, celebration, or rocket reactions on the PR

PRs created within the last month are not affected by this cleanup.

If you believe this PR was closed incorrectly, or if you are still actively working on it, please leave a comment explaining why it should be reopened. A maintainer can review and reopen it if appropriate.

Thanks again for taking the time to contribute.

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

Labels

2.0 automated-pr-cleanup contributor core Anything pertaining to core functionality of the application (opencode server stuff)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant