Describe the bug
The semantic session manager and the semantic service each get their own CachingSemanticConfigStorage over the same config database. Set type writes go through the session manager's instance, but set configs, which carry the categories a set inherits from its set type, are read and cached through the service's instance. Writes through one instance never invalidate the other. So after a set type is deleted, ingestion keeps extracting with its deleted categories; after a set is registered to a new set type, ingestion does not see that type's categories. Both last until a later write through the service's instance happens to clear its set config cache (a category, tag or category template change), or until the process restarts.
As of 82b6c6f58 (main), paths under packages/server/src/memmachine_server/:
common/resource_manager/semantic_manager.py:175-194: get_semantic_config_storage builds a new SemanticConfigStorageSqlAlchemy, wrapped in a new CachingSemanticConfigStorage when with_config_cache is set (the default, common/configuration/__init__.py:154-157), on every call. It is called once for the service (:242) and again for the session manager (:268).
semantic_memory/config_store/caching_semantic_config_storage.py:20-25 states that "Cache entries are invalidated on write operations"; each write invalidates only its own instance's dictionaries (for example delete_set_type_id, :336-346, and register_set_id_set_type, :97-117).
- The session manager writes set types through its instance:
add_set_type_id (semantic_memory/semantic_session_manager.py:484, :503), delete_set_type_id (:516), register_set_id_set_type (:388, :646).
- The service reads set configs through the other instance:
_set_id_resource (semantic_memory/semantic_memory.py:505), which supplies ingestion's categories and get_set_id_category_names, and _set_ids_embedders (:476). A set config includes the categories inherited from the set's registered set type (semantic_memory/config_store/config_store_sqlalchemy.py:317-337).
Steps to reproduce
Build the real SemanticResourceManager (SQLite config and feature databases, mock embedder and language model) and call the public session manager methods behind /memories/semantic/set_type, /memories/semantic/set_type/delete and /memories/semantic/category/template:
session_manager = await manager.get_semantic_session_manager()
service = await manager.get_semantic_service()
type_1 = await session_manager.create_set_type(session_data=session, metadata_tags=["topic"])
await session_manager.add_category_template(set_type_id=type_1, category_name="travel_plans", prompt="p", description=None)
set_id = await session_manager.get_set_id(session_data=session, set_metadata_keys=["topic"], set_metadata={"topic": "x"})
await session_manager.delete_set_type(set_type_id=type_1)
type_2 = await session_manager.create_set_type(session_data=session, metadata_tags=["topic"])
await session_manager.add_category_template(set_type_id=type_2, category_name="budget", prompt="p", description=None)
await session_manager.get_set_id_category_names(set_id=set_id) # any read, e.g. an ingestion pass
await session_manager.get_set_id(session_data=session, set_metadata_keys=["topic"], set_metadata={"topic": "x"})
Measured, comparing get_set_id_category_names (served) with an uncached SemanticConfigStorageSqlAlchemy read of the same set (stored):
service and session manager share one config store: False
before delete (served, stored): (['travel_plans'], ['travel_plans'])
after delete (served, stored): (['travel_plans'], [])
after re-register (served, stored): ([], ['budget'])
Not run: a full server with background ingestion. Ingestion reads the same _set_id_resource on every pass (semantic_memory/semantic_ingestion.py:123).
Expected behavior
One process has one semantic config cache. SemanticResourceManager builds the config storage once and gives the same instance to the service and the session manager, so every write invalidates the cache that serves later reads, and a set's categories reflect its current set type.
Environment
- MemMachine:
main at 82b6c6f58
- Python 3.14.3, SQLite 3.50.4, macOS
semantic_memory.with_config_cache: true (the default)
🤖 Written by Claude Code (Claude Opus 5.5) on behalf of @edwinyyyu.
Describe the bug
The semantic session manager and the semantic service each get their own
CachingSemanticConfigStorageover the same config database. Set type writes go through the session manager's instance, but set configs, which carry the categories a set inherits from its set type, are read and cached through the service's instance. Writes through one instance never invalidate the other. So after a set type is deleted, ingestion keeps extracting with its deleted categories; after a set is registered to a new set type, ingestion does not see that type's categories. Both last until a later write through the service's instance happens to clear its set config cache (a category, tag or category template change), or until the process restarts.As of
82b6c6f58(main), paths underpackages/server/src/memmachine_server/:common/resource_manager/semantic_manager.py:175-194:get_semantic_config_storagebuilds a newSemanticConfigStorageSqlAlchemy, wrapped in a newCachingSemanticConfigStoragewhenwith_config_cacheis set (the default,common/configuration/__init__.py:154-157), on every call. It is called once for the service (:242) and again for the session manager (:268).semantic_memory/config_store/caching_semantic_config_storage.py:20-25states that "Cache entries are invalidated on write operations"; each write invalidates only its own instance's dictionaries (for exampledelete_set_type_id,:336-346, andregister_set_id_set_type,:97-117).add_set_type_id(semantic_memory/semantic_session_manager.py:484,:503),delete_set_type_id(:516),register_set_id_set_type(:388,:646)._set_id_resource(semantic_memory/semantic_memory.py:505), which supplies ingestion's categories andget_set_id_category_names, and_set_ids_embedders(:476). A set config includes the categories inherited from the set's registered set type (semantic_memory/config_store/config_store_sqlalchemy.py:317-337).Steps to reproduce
Build the real
SemanticResourceManager(SQLite config and feature databases, mock embedder and language model) and call the public session manager methods behind/memories/semantic/set_type,/memories/semantic/set_type/deleteand/memories/semantic/category/template:Measured, comparing
get_set_id_category_names(served) with an uncachedSemanticConfigStorageSqlAlchemyread of the same set (stored):Not run: a full server with background ingestion. Ingestion reads the same
_set_id_resourceon every pass (semantic_memory/semantic_ingestion.py:123).Expected behavior
One process has one semantic config cache.
SemanticResourceManagerbuilds the config storage once and gives the same instance to the service and the session manager, so every write invalidates the cache that serves later reads, and a set's categories reflect its current set type.Environment
mainat82b6c6f58semantic_memory.with_config_cache: true(the default)🤖 Written by Claude Code (Claude Opus 5.5) on behalf of @edwinyyyu.