fix(store): avoid mutating published injected layer lists

This commit is contained in:
Sylvester Kaczmarek
2026-08-11 17:46:29 +01:00
parent 4518cb0b23
commit 9c3673af34
2 changed files with 65 additions and 5 deletions
+6 -5
View File
@@ -137,15 +137,16 @@ def inject_layer_data(
tagged.append(entry)
with _data_lock:
existing = latest_data.get(layer)
if not isinstance(existing, list):
existing = []
current = latest_data.get(layer)
existing = list(current) if isinstance(current, list) else []
if mode == "replace":
existing = [e for e in existing if not e.get("_injected")]
existing.extend(tagged)
latest_data[layer] = existing
# Readers can hold references to published layer lists after releasing
# _data_lock. Build a fresh list and swap it atomically rather than
# mutating the published object in place with list.extend().
latest_data[layer] = [*existing, *tagged]
bump_data_version()
@@ -0,0 +1,59 @@
"""Regression coverage for copy-on-write OpenClaw layer injection."""
from services import ai_intel_store
from services.fetchers import _store
def _publish_test_layer(monkeypatch, items):
published = list(items)
monkeypatch.setitem(_store.latest_data, "air_quality", published)
monkeypatch.setattr(_store, "bump_data_version", lambda: None)
return published
def test_append_does_not_mutate_previously_published_list(monkeypatch):
before = _publish_test_layer(monkeypatch, [{"id": "existing"}])
result = ai_intel_store.inject_layer_data(
"air_quality",
[{"id": "injected"}],
mode="append",
)
after = _store.latest_data["air_quality"]
assert result == {
"ok": True,
"layer": "air_quality",
"injected": 1,
"mode": "append",
}
assert after is not before
assert before == [{"id": "existing"}]
assert [item["id"] for item in after] == ["existing", "injected"]
assert after[-1]["_injected"] is True
assert after[-1]["_source"] == "user:openclaw"
def test_replace_does_not_mutate_previously_published_list(monkeypatch):
before = _publish_test_layer(
monkeypatch,
[
{"id": "native"},
{"id": "old-injected", "_injected": True},
],
)
result = ai_intel_store.inject_layer_data(
"air_quality",
[{"id": "new-injected"}],
mode="replace",
)
after = _store.latest_data["air_quality"]
assert result["ok"] is True
assert after is not before
assert before == [
{"id": "native"},
{"id": "old-injected", "_injected": True},
]
assert [item["id"] for item in after] == ["native", "new-injected"]