From 34e7c93f7894f5e331b40aed7834202bcbc91c1c Mon Sep 17 00:00:00 2001 From: Michael Panchenko Date: Mon, 8 Jun 2026 00:03:49 +0300 Subject: [PATCH] Fix: never persist runtime config overrides during project registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: `serena start-mcp-server --language-backend X --project ` silently rewrote the global serena_config.yml's language_backend to X. Cause: a not-yet-registered --project is auto-registered, and SerenaConfig.save() rewrote the WHOLE file from the in-memory config — which by then carried the CLI --language-backend override (and would equally carry --log-level / --tool-timeout / etc.). Fix: project (de)registration now persists ONLY the projects list. add_registered_project / remove_project go through a new _persist_projects(), which re-loads the on-disk config, copies in the current project list, and saves that — so a transient runtime override of any field can never be written back. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 1 + src/serena/config/serena_config.py | 22 +++++++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c89f63de..48c040e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Status of the `main` branch. Changes prior to the next official version change w or a `.git`, including worktree/submodule pointer files), instead of preferring an ancestor's `.serena/project.yml` over a closer `.git`. This previously bound CLI agents (Claude Code, Codex, Gemini) launched from inside a worktree to the parent repo, causing stale reads and misdirected edits. + - Fix: some CLI flags on start-mcp-server modified the global config (notably, language-backend). * Language Servers: - `typescript_vts`: Add `initialization_options` setting in `ls_specific_settings.typescript_vts`. diff --git a/src/serena/config/serena_config.py b/src/serena/config/serena_config.py index 03a18e72..92b2060d 100644 --- a/src/serena/config/serena_config.py +++ b/src/serena/config/serena_config.py @@ -1035,10 +1035,10 @@ class SerenaConfig(SharedConfig, ModeSelectionDefinitionWithBaseModes): def add_registered_project(self, registered_project: RegisteredProject) -> None: """ - Adds a registered project, saving the configuration file. + Adds a registered project, persisting only the updated project list (see :meth:`_persist_projects`). """ self.projects.append(registered_project) - self.save() + self._persist_projects() def add_project_from_path(self, project_root: Path | str) -> "Project": """ @@ -1083,7 +1083,23 @@ class SerenaConfig(SharedConfig, ModeSelectionDefinitionWithBaseModes): break else: raise ValueError(f"Project '{project_name}' not found in Serena configuration; valid project names: {self.project_names}") - self.save() + self._persist_projects() + + def _persist_projects(self) -> None: + """ + Persist ONLY the registered-projects list, leaving every other setting at its on-disk value. + + Project (de)registration can happen while a session is running with transient runtime overrides applied + to this in-memory instance (e.g. ``start-mcp-server --language-backend`` / ``--log-level``). A full + :meth:`save` would write those overrides back to the global config, silently clobbering the user's + settings. Instead we re-load the persisted config, copy in the current project list, and save that — so + only ``projects`` is ever mutated on disk. + """ + if self.config_file_path is None: + return + persisted = SerenaConfig.from_config_file() + persisted.projects = list(self.projects) + persisted.save() def save(self) -> None: """