feat: show zone resolver diagnostics

This commit is contained in:
2026-05-25 00:09:18 +02:00
parent f1cd74dc7b
commit 1ad432270b
6 changed files with 126 additions and 3 deletions

View File

@@ -332,6 +332,7 @@ def resolve_zones(
edge_records,
enabled_definitions,
seed_node_ids_by_zone_id,
diagnostics,
)
for candidate in attraction_candidates:
candidates_by_node_id[candidate.node_id].append(candidate)
@@ -406,6 +407,16 @@ def resolve_zones(
internal_edge_ids_by_zone_id[source_zone_id].append(edge.id)
continue
if source_zone_id or target_zone_id:
zone_ids = tuple(sorted(str(zone_id) for zone_id in {source_zone_id, target_zone_id} - {None}))
diagnostics.append(
ZoneDiagnostic(
severity="INFO",
code="ZONE_EDGE_CROSSES_ZONE_BOUNDARY",
message=f"Edge {edge.id} crosses a zone boundary.",
edge_id=edge.id,
zone_ids=zone_ids,
)
)
boundary_edges.append(
ZoneBoundaryEdge(
edge_id=edge.id,
@@ -468,6 +479,7 @@ def _attraction_candidates(
edge_records: tuple[_EdgeRecord, ...],
enabled_definitions: tuple[tuple[int, ZoneDefinition], ...],
seed_node_ids_by_zone_id: Mapping[str, set[str]],
diagnostics: list[ZoneDiagnostic],
) -> list[_Candidate]:
nodes_by_id = {node.id: node for node in node_records}
adjacency: dict[str, list[_EdgeRecord]] = defaultdict(list)
@@ -476,6 +488,7 @@ def _attraction_candidates(
adjacency[edge.target].append(edge)
candidates: dict[tuple[str, str], _Candidate] = {}
depth_limit_diagnostics: set[tuple[str, str]] = set()
for definition_order, definition in enabled_definitions:
seed_node_ids = seed_node_ids_by_zone_id.get(definition.id, set())
if not seed_node_ids:
@@ -486,6 +499,27 @@ def _attraction_candidates(
while queue:
node_id, depth = queue.popleft()
if depth >= rule.depth:
if _has_matching_attraction_neighbor(
node_id,
adjacency.get(node_id, []),
nodes_by_id,
rule,
):
key = (definition.id, node_id)
if key not in depth_limit_diagnostics:
depth_limit_diagnostics.add(key)
diagnostics.append(
ZoneDiagnostic(
severity="INFO",
code="ZONE_ATTRACTION_DEPTH_LIMIT_REACHED",
message=(
f"Zone {definition.id} reached attraction depth "
f"{rule.depth} at node {node_id}."
),
node_id=node_id,
zone_ids=(definition.id,),
)
)
continue
for edge in adjacency.get(node_id, []):
if not _edge_matches_attraction_rule(edge, rule):
@@ -521,6 +555,24 @@ def _attraction_candidates(
return sorted(candidates.values(), key=lambda candidate: (candidate.node_id, candidate.zone_id))
def _has_matching_attraction_neighbor(
node_id: str,
edges: Iterable[_EdgeRecord],
nodes_by_id: Mapping[str, _NodeRecord],
rule: ZoneAttractionRule,
) -> bool:
for edge in edges:
if not _edge_matches_attraction_rule(edge, rule):
continue
neighbor_id = _neighbor_for_direction(node_id, edge, rule.direction)
if not neighbor_id:
continue
neighbor = nodes_by_id.get(neighbor_id)
if neighbor and _rule_matches(neighbor.data, rule.node_filter, empty_matches=True):
return True
return False
def _node_records(nodes: Iterable[Mapping[str, Any]]) -> tuple[_NodeRecord, ...]:
records: list[_NodeRecord] = []
for order, element in enumerate(nodes):