Summary
palace_graph.create_tunnel validates that wing/room names are non-empty strings (via _require_name) but never confirms that the named rooms actually contain any drawers. Pointing an explicit tunnel at a phantom room — agent fabricates a room name it expects to exist, typo in a slug, scaffolding before mining — silently succeeds and persists.
Combined with #1467 (tunnel-file ignores palace_path), this lets an isolated agent both write and read its own bogus tunnel and self-confirm "tunnel created" in completion reports.
Reproduction
import mempalace.mcp_server as ms
ms.tool_add_drawer("wing_real", "real_room", "content")
# target_room is wrong, but create_tunnel doesn't care:
result = ms.tool_create_tunnel("wing_real", "real_room", "wing_real", "phantom_room")
print(result) # success, tunnel persisted, pointing at a room with zero drawers
Fix
PR #TBD. For kind == "explicit", create_tunnel now queries the chroma collection with col.get(where={"$and": [{"wing": W}, {"room": R}]}, limit=1, include=[]) for both endpoints. Zero rows → raises ValueError naming the offending endpoint.
Three carve-outs preserved:
kind != "explicit" — topic tunnels use synthetic topic:<name> room IDs that don't appear in chroma. Skip.
_get_collection returns None (palace not yet created, tests, transient outage) — skip rather than fail-closed, matches palace_graph tolerance elsewhere.
- Query raises — log and treat as "can't verify, allow" so a flaky index doesn't block legitimate writes.
Behaviour change: existing callers that previously created tunnels pointing at empty rooms as scaffolding will now raise. File the drawer first, then create the tunnel — the order the docs have always recommended.
Summary
palace_graph.create_tunnelvalidates that wing/room names are non-empty strings (via_require_name) but never confirms that the named rooms actually contain any drawers. Pointing an explicit tunnel at a phantom room — agent fabricates a room name it expects to exist, typo in a slug, scaffolding before mining — silently succeeds and persists.Combined with #1467 (tunnel-file ignores
palace_path), this lets an isolated agent both write and read its own bogus tunnel and self-confirm "tunnel created" in completion reports.Reproduction
Fix
PR #TBD. For
kind == "explicit",create_tunnelnow queries the chroma collection withcol.get(where={"$and": [{"wing": W}, {"room": R}]}, limit=1, include=[])for both endpoints. Zero rows → raisesValueErrornaming the offending endpoint.Three carve-outs preserved:
kind != "explicit"— topic tunnels use synthetictopic:<name>room IDs that don't appear in chroma. Skip._get_collectionreturnsNone(palace not yet created, tests, transient outage) — skip rather than fail-closed, matchespalace_graphtolerance elsewhere.Behaviour change: existing callers that previously created tunnels pointing at empty rooms as scaffolding will now raise. File the drawer first, then create the tunnel — the order the docs have always recommended.