Skip to content

Conversation

@shuv1337
Copy link
Collaborator

Implements runtime configuration updates through a PATCH /config endpoint with optional hot-reload capability controlled by OPENCODE_CONFIG_HOT_RELOAD environment variable.

Changes

Features

  • PATCH /config endpoint for updating project or global configuration at runtime
  • File locking mechanism with 30s timeout to prevent concurrent write conflicts
  • Automatic backup and restore on configuration update failures
  • Config invalidation system that triggers targeted cache invalidation and event publishing
  • Event bus integration for config.updated notifications across project instances
  • Deep merge support for partial configuration updates with JSONC comment preservation

Infrastructure

  • New config write module with atomic file operations and validation
  • Lock file management for concurrent access control
  • Configuration diffing to detect actual changes before triggering invalidations
  • State-based hot reload that respects feature flag for backward compatibility
  • Integration with agent, provider, server, mcp, lsp and plugin systems for config subscription

Testing

  • Hot-reload test suite covering GET/PATCH workflows
  • Lock file management tests
  • Config write and persistence tests

Documentation

  • Complete PATCH /config specification with examples
  • Feature flag documentation for OPENCODE_CONFIG_HOT_RELOAD
  • Client integration guide with curl examples

Breaking Changes

None. Hot-reload is opt-in via environment variable. Default behavior maintains existing cache semantics.

Configuration

Set OPENCODE_CONFIG_HOT_RELOAD=true before starting server to enable runtime config updates without restart. Without this flag, disk writes persist but in-memory cache remains until process restart.

shuv1337 and others added 19 commits November 12, 2025 13:45
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…nit (#13)

* Initial plan

* Fix race condition in ConfigInvalidation.setup() using Promise-based pattern

Co-authored-by: kcrommett <523952+kcrommett@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: kcrommett <523952+kcrommett@users.noreply.github.com>
@shuv1337 shuv1337 merged commit 38376f4 into config-hot-reload Nov 13, 2025
1 of 2 checks passed
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +114 to +122
let releaseFn: () => void
const lockPromise = new Promise<void>((resolve) => {
releaseFn = resolve
})

fileLocks.set(normalized, lockPromise)

const releaseFilesystem = await acquireFilesystemLock({
filepath: normalized,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove in-memory lock when filesystem acquisition fails

If acquireFilesystemLock throws (e.g., permission issues or a stale .lock file that can’t be removed), the code has already inserted normalized into fileLocks with an unresolved promise. The exception bubbles out before the entry is cleaned up or the promise resolved, so every later caller of acquireLock will block forever awaiting the stale promise and the lock can never be recovered without restarting the process. Wrap the filesystem acquisition in a try/finally (or catch) to delete the map entry and resolve the promise on error.

Useful? React with 👍 / 👎.

shuv1337 added a commit that referenced this pull request Nov 14, 2025
* PATCH /config hot-reload w/ selective cache invalidation (#15)

PATCH /config hot-reload w/ selective cache invalidation

* typecheck fix

* chore: format code

* fix for failing test

* fix for failing test

* chore: format code

---------

Co-authored-by: GitHub Action <action@github.com>
shuv1337 added a commit that referenced this pull request Nov 17, 2025
* PATCH /config hot-reload w/ selective cache invalidation (#15)

PATCH /config hot-reload w/ selective cache invalidation

* typecheck fix

* chore: format code

* fix for failing test

* fix for failing test

* chore: format code

* chore: format code

* Fix cache corruption from partial updates in file refresh (#18)

* Fix cache corruption bug in refresh function

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
@shuv1337 shuv1337 deleted the pr-config-reload-merge branch November 21, 2025 00:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants