fix(graph): normalize wing slug at init so topic tunnels fire for hyphenated dirs (#1194)
`init` was recording `topics_by_wing[<raw-dirname>]` while `mempalace.yaml` got the lower-cased separator-collapsed slug. At mine time the miner read the slug from the yaml and missed the registry key, so `_compute_topic_tunnels_for_wing` returned 0 silently for every project whose folder contained a `-` or a space — the most common shape in the wild. Extracted the rule into `config.normalize_wing_name()` and routed both `cli.cmd_init` (registry write) and `room_detector_local.detect_rooms_local` (yaml write) through it. Added a regression test in `test_cli.py` asserting the registry call uses the normalized slug, plus four direct unit tests for the helper. Refs #1180. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -138,6 +138,38 @@ def test_cmd_init_with_entities(mock_config_cls, tmp_path):
|
||||
cmd_init(args)
|
||||
|
||||
|
||||
@patch("mempalace.cli.MempalaceConfig")
|
||||
def test_cmd_init_normalizes_wing_name_for_topics_registry(mock_config_cls, tmp_path):
|
||||
"""Regression for #1194: hyphenated dir names must be normalized to the
|
||||
same slug ``mempalace.yaml`` uses, otherwise ``topics_by_wing`` keys
|
||||
miss the miner's lookup at mine time and tunnels are silently dropped.
|
||||
"""
|
||||
project = tmp_path / "my-cool-app"
|
||||
project.mkdir()
|
||||
fake_files = [project / "a.txt"]
|
||||
detected = {
|
||||
"people": [{"name": "Alice"}],
|
||||
"projects": [],
|
||||
"topics": [{"name": "Bun"}],
|
||||
"uncertain": [],
|
||||
}
|
||||
confirmed = {"people": ["Alice"], "projects": [], "topics": ["Bun"]}
|
||||
args = argparse.Namespace(dir=str(project), yes=True)
|
||||
with (
|
||||
patch("mempalace.entity_detector.scan_for_detection", return_value=fake_files),
|
||||
patch("mempalace.entity_detector.detect_entities", return_value=detected),
|
||||
patch("mempalace.entity_detector.confirm_entities", return_value=confirmed),
|
||||
patch("mempalace.miner.add_to_known_entities") as mock_register,
|
||||
patch("mempalace.room_detector_local.detect_rooms_local"),
|
||||
patch("builtins.open", MagicMock()),
|
||||
patch("mempalace.cli._maybe_run_mine_after_init"),
|
||||
):
|
||||
mock_register.return_value = "/tmp/known_entities.json"
|
||||
cmd_init(args)
|
||||
mock_register.assert_called_once()
|
||||
assert mock_register.call_args.kwargs["wing"] == "my_cool_app"
|
||||
|
||||
|
||||
@patch("mempalace.cli.MempalaceConfig")
|
||||
def test_cmd_init_with_entities_zero_total(mock_config_cls, tmp_path, capsys):
|
||||
"""When entities detected but total is 0, prints 'No entities' message."""
|
||||
|
||||
+20
-1
@@ -3,7 +3,7 @@ import json
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
from mempalace.config import MempalaceConfig, sanitize_kg_value, sanitize_name
|
||||
from mempalace.config import MempalaceConfig, normalize_wing_name, sanitize_kg_value, sanitize_name
|
||||
|
||||
|
||||
def test_default_config():
|
||||
@@ -110,6 +110,25 @@ def test_init():
|
||||
assert os.path.exists(os.path.join(tmpdir, "config.json"))
|
||||
|
||||
|
||||
# --- normalize_wing_name ---
|
||||
|
||||
|
||||
def test_normalize_wing_name_hyphen():
|
||||
assert normalize_wing_name("mempal-private") == "mempal_private"
|
||||
|
||||
|
||||
def test_normalize_wing_name_space():
|
||||
assert normalize_wing_name("My Project") == "my_project"
|
||||
|
||||
|
||||
def test_normalize_wing_name_already_clean():
|
||||
assert normalize_wing_name("memorymark") == "memorymark"
|
||||
|
||||
|
||||
def test_normalize_wing_name_mixed():
|
||||
assert normalize_wing_name("My-Cool App") == "my_cool_app"
|
||||
|
||||
|
||||
# --- sanitize_name ---
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user