fix: remove hardcoded "main" default from default_project (#575)

When a user's config.json had projects with names other than "main" and
no explicit default_project key, the hardcoded field default of "main"
would not match any project. The model_post_init fixup logic existed but
was untested and only handled the stale-name case, not the None case.

Changes:
- Change default_project field default from "main" to None
- Use model_fields_set to distinguish "config omitted the key" (auto-resolve
  to first project) from "user explicitly set None" (preserve for discovery mode)
- Split model_post_init into two branches: auto-resolve when not explicitly
  provided, correct stale names when explicitly set but invalid
- Remove # pragma: no cover from now-tested branches
- Add 10 new tests covering valid defaults, stale defaults, empty string,
  single project, config file round-trips, and discovery mode preservation

Fixes #575

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
This commit is contained in:
phernandez
2026-02-23 21:59:15 -06:00
parent da4d369c32
commit 18f00f861d
2 changed files with 137 additions and 9 deletions
+16 -9
View File
@@ -132,7 +132,7 @@ class BasicMemoryConfig(BaseSettings):
description="Mapping of project names to their ProjectEntry configuration",
)
default_project: Optional[str] = Field(
default="main",
default=None,
description="Name of the default project to use. When set, acts as fallback when no project parameter is specified. Set to null to disable automatic project resolution.",
)
@@ -493,18 +493,25 @@ class BasicMemoryConfig(BaseSettings):
if self.database_backend == DatabaseBackend.POSTGRES: # pragma: no cover
return # pragma: no cover
# Ensure at least one project exists; if none exist then create main
if not self.projects: # pragma: no cover
# Trigger: no projects configured (fresh install or empty config)
# Why: every config needs at least one project to be functional
# Outcome: creates "main" project using BASIC_MEMORY_HOME or ~/basic-memory
if not self.projects:
self.projects["main"] = ProjectEntry(
path=str(Path(os.getenv("BASIC_MEMORY_HOME", Path.home() / "basic-memory")))
)
# Ensure default project is valid (i.e. points to an existing project)
# None means "no default" — intentionally left unset
if (
self.default_project is not None and self.default_project not in self.projects
): # pragma: no cover
# Set default to first available project
# Trigger: default_project was not explicitly provided in the input data
# (config file omitted the key, or BasicMemoryConfig() called with no args)
# Why: callers like get_project_config() expect a valid project name;
# but explicit None (discovery mode) must be preserved
# Outcome: sets default_project to the first available project
if "default_project" not in self.model_fields_set:
self.default_project = next(iter(self.projects.keys()))
# Trigger: default_project was explicitly set but references a non-existent project
# Why: project may have been removed or renamed since config was saved
# Outcome: corrects to the first available project
elif self.default_project is not None and self.default_project not in self.projects:
self.default_project = next(iter(self.projects.keys()))
@property