Private
Public Access
2.2 KiB
2.2 KiB
Config I/O State Ownership
Rule: The AppController is the single source of truth for the
in-memory config (self.config) and the only authorized caller of
the file I/O primitives (the config-load/save helpers, which moved out
of src/models.py to src/project_manager.py per
module_taxonomy_refactor_20260627).
Why
- The controller owns the in-memory state. If other modules
write to
config.tomldirectly, the controller'sself.configsilently drifts from disk. Tests can corrupt the user's TOML files; users lose data without warning. - Test isolation breaks. When
models.save_config(...)is called from anywhere insrc/, tests cannot intercept the write without patching the I/O primitive. The test then couples to the file format, not the controller's behavior. - Path resolution can't be enforced. The controller respects
SLOP_CONFIGenv var at call time. Direct calls tomodels.save_configwould only respect it if the path is re-resolved (which it is in_save_config_to_disk, but only because someone remembered).
What is Forbidden in src/
models.load_config(...)(legacy public function)models.save_config(...)(legacy public function)models._load_config_from_disk(...)(private I/O primitive)models._save_config_to_disk(...)(private I/O primitive)
The only allowed call sites are inside AppController itself
(load_config() and save_config() methods).
The Public API
# In AppController:
def load_config(self) -> Dict[str, Any]:
"""Re-read the global config.toml from disk and update self.config."""
self.config = models._load_config_from_disk()
return self.config
def save_config(self) -> None:
"""Flush self.config to disk."""
models._save_config_to_disk(self.config)
Callers (including gui_2.py, commands.py, etc.) go through
the controller:
# In App class methods (gui_2.py): __getattr__ delegates to controller
self.save_config() # -> controller.save_config()
app.save_config() # -> controller.save_config() (via __getattr__)
app.load_config() # -> controller.load_config() (via __getattr__)
# In AppController:
self.save_config() # direct
self.load_config() # direct