Refinements to the dependency exploration ui

This commit is contained in:
2026-05-04 12:17:48 +02:00
parent 07c9a95efc
commit 356c0026ef
8 changed files with 408 additions and 36 deletions

View File

@@ -1101,6 +1101,7 @@ class RegistryService:
profile_id: int | None = None,
rules: list[dict[str, Any]] | None = None,
manual_overrides: dict[str, str] | None = None,
use_latest_profile: bool = True,
) -> dict[str, object]:
impact = None
if base_analysis_run_id is not None or target_analysis_run_id is not None:
@@ -1122,9 +1123,7 @@ class RegistryService:
)
changed_fact_keys = set(impact.changed_fact_keys) if impact is not None else set()
ability_map = self.store.get_ability_map(repository_id)
facts_by_id = {
fact.id: fact for fact in self.store.list_observed_facts(repository_id)
}
facts_by_id = {fact.id: fact for fact in self.store.list_observed_facts(repository_id)}
characteristic_index = self._dependency_characteristic_index(ability_map)
nodes: dict[str, dict[str, object]] = {}
edge_sources: dict[str, DependencyEdge] = {}
@@ -1132,6 +1131,8 @@ class RegistryService:
profile = (
self.store.get_dependency_graph_profile(repository_id, profile_id)
if profile_id is not None
else self.store.latest_dependency_graph_profile(repository_id)
if use_latest_profile and not rules and not manual_overrides
else None
)
merged_rules = [*(profile.filter_rules if profile is not None else []), *(rules or [])]
@@ -1139,6 +1140,12 @@ class RegistryService:
**(profile.manual_overrides if profile is not None else {}),
**(manual_overrides or {}),
}
graph_edges = [
display_edge
for edge in graph.edges
if (display_edge := self._dependency_display_edge(edge, facts_by_id))
is not None
]
def ensure_node(kind: str, key: str, item_id: int | None) -> None:
if key in nodes:
@@ -1150,6 +1157,11 @@ class RegistryService:
if fact is not None:
detail = {
"name": fact.name,
"label": (
f"{fact.path} ({fact.kind})"
if key.startswith("fact:document:")
else f"{fact.name} ({fact.kind}, {fact.path})"
),
"description": fact.value,
"primaryClass": fact.metadata.get("source_role", fact.kind),
"attributes": self._dependency_fact_attributes(fact),
@@ -1174,13 +1186,16 @@ class RegistryService:
"stableKey": key,
"kind": kind,
"layer": self._dependency_layer(kind),
"label": self._dependency_node_label(repository_id, kind, key, item_id),
"label": detail.get("label")
or self._dependency_node_label(repository_id, kind, key, item_id),
"reviewState": "accepted",
"name": detail.get("name")
or self._dependency_node_label(repository_id, kind, key, item_id),
"description": detail.get("description", ""),
"primaryClass": detail.get("primaryClass", kind),
"attributes": detail.get("attributes", []),
"confidence": detail.get("confidence"),
"visualSize": self._dependency_node_size(detail.get("confidence")),
"ownership": self._ownership_for_kind(kind),
"freshnessState": (
impact_item.freshness_state
@@ -1212,12 +1227,12 @@ class RegistryService:
),
}
for edge in graph.edges:
for edge in graph_edges:
ensure_node(edge.source_kind, edge.source_key, edge.source_id)
ensure_node(edge.target_kind, edge.target_key, edge.target_id)
edges = []
for index, edge in enumerate(graph.edges):
for index, edge in enumerate(graph_edges):
edge_id = f"{edge.source_key}->{edge.target_key}:{index}"
source_data = nodes[edge.source_key]["data"]
target_data = nodes[edge.target_key]["data"]
@@ -1230,6 +1245,7 @@ class RegistryService:
"stableKey": edge_id,
"kind": "edge",
"layer": "dependency",
"reviewState": "accepted",
"source": edge.source_key,
"target": edge.target_key,
"sourceKind": edge.source_kind,
@@ -1238,6 +1254,7 @@ class RegistryService:
"targetLayer": self._dependency_layer(edge.target_kind),
"dependencyType": edge.dependency_type,
"strength": edge.strength,
"edgeWidth": self._dependency_edge_width(edge.strength),
"edgeSource": edge.source,
"sameLayer": edge.same_layer,
"freshnessState": (
@@ -1284,6 +1301,11 @@ class RegistryService:
for element in nodes.values()
if visibility[element["data"]["id"]]["displayState"] == "hide"
}
blurred_node_ids = {
element["data"]["id"]
for element in nodes.values()
if visibility[element["data"]["id"]]["displayState"] == "blur"
}
visible_elements: list[dict[str, object]] = []
hidden_elements: list[dict[str, object]] = []
orphaned_overrides = sorted(
@@ -1302,12 +1324,21 @@ class RegistryService:
"displayState": "hide",
"visibilityReason": "connected-node-hidden",
}
connected_to_blurred = (
"source" in element["data"]
and (
element["data"]["source"] in blurred_node_ids
or element["data"]["target"] in blurred_node_ids
)
)
element["data"].update(state)
element["data"]["connectedToBlurred"] = connected_to_blurred
element["classes"] = " ".join(
part
for part in (
element.get("classes", ""),
f"display-{state['displayState']}",
"connects-blurred" if connected_to_blurred else "",
"manual-override" if state["visibilitySource"] == "manual" else "",
"rule-derived" if state["visibilitySource"] == "rule" else "",
)
@@ -2578,6 +2609,48 @@ class RegistryService:
attributes.append(value)
return sorted(set(attributes))
def _dependency_display_edge(
self,
edge: DependencyEdge,
facts_by_id: dict[int, ObservedFact],
) -> DependencyEdge | None:
if edge.source_kind != "fact" or edge.source_id is None:
return edge
fact = facts_by_id.get(edge.source_id)
if fact is None:
return edge
if self._suppress_dependency_fact(fact):
return None
display_key = self._dependency_fact_display_key(fact)
if display_key == edge.source_key:
return edge
return replace(edge, source_key=display_key)
def _suppress_dependency_fact(self, fact: ObservedFact) -> bool:
return (
fact.path.lower().endswith("scope.md")
and fact.metadata.get("source_role") == "derived_scope"
)
def _dependency_fact_display_key(self, fact: ObservedFact) -> str:
document_paths = {"readme.md", "scope.md"}
if fact.path.lower() in document_paths and fact.kind in {
"documentation",
"intent",
"scope",
}:
return f"fact:document:{fact.path}"
return f"fact:{fact.kind}:{fact.path}:{fact.name}"
def _dependency_node_size(self, confidence: object) -> int:
if not isinstance(confidence, int | float):
return 36
bounded = max(0.0, min(float(confidence), 1.0))
return int(28 + (bounded * 28))
def _dependency_edge_width(self, strength: str) -> int:
return {"weak": 1, "medium": 3, "strong": 5}.get(strength, 2)
def _dependency_layer(self, kind: str) -> str:
if kind in {"fact", "evidence", "feature", "capability", "ability", "scope"}:
return kind
@@ -2642,6 +2715,8 @@ class RegistryService:
actual = data.get(key)
if key == "dependencyType":
actual = data.get("dependencyType")
elif key == "reviewState":
actual = data.get("reviewState")
elif key == "sameLayer":
actual = bool(data.get("sameLayer"))
elif key == "attributes":