feat(graph): namespace topic-tunnel rooms with "topic:" prefix + kind field

Previously a cross-wing topic tunnel for "Angular" stored the room as
"Angular" — colliding with a wing's literal folder-derived "Angular" room
at follow_tunnels/list_tunnels read time, and exposing raw topic strings
(which may contain characters rejected by sanitize_name) to the MCP
surface.

Topic tunnels now store their room as "topic:<original-casing>" and carry
kind="topic" on the stored dict. Explicit tunnels get kind="explicit"
(default). follow_tunnels("wing", "Angular") on a literal Angular room
no longer surfaces topic connections for the same name, and any LLM
scanning list_tunnels has a visible discriminator.
This commit is contained in:
Igor Lins e Silva
2026-04-24 23:05:56 -03:00
parent fe051adc73
commit 865a36bc5c
4 changed files with 79 additions and 10 deletions
+4 -1
View File
@@ -536,7 +536,10 @@ def test_mine_creates_topic_tunnels_for_shared_topics(tmp_path, monkeypatch):
listed = palace_graph.list_tunnels()
assert len(listed) == 1
rooms = {listed[0]["source"]["room"], listed[0]["target"]["room"]}
assert rooms == {"foo"}
# Topic tunnels use a ``topic:<name>`` synthetic room so they can't
# collide with literal folder-derived rooms of the same name.
assert rooms == {"topic:foo"}
assert listed[0]["kind"] == "topic"
wings = {listed[0]["source"]["wing"], listed[0]["target"]["wing"]}
assert wings == {"wing_one", "wing_two"}