1
0
Fork 0
Code Issues Pull requests Projects Releases 2 Packages Wiki Activity Actions Pages

Add function-scoped Logic visualization

This commit is contained in:
Andraxion 2026-07-25 21:08:43 -04:00
parent 9fcafc290c
commit 9b4258c852
22 changed files with 1420 additions and 62 deletions

View file

@ -34,7 +34,7 @@ from .errors import DocForgeError
from .index import APPLICATION_ID, INDEX_SCHEMA_VERSION, ProjectIndex, re_tokenize
from .project import project_root_fingerprint
VISUALIZATION_TEMPLATE = "graph-browser@15"
VISUALIZATION_TEMPLATE = "graph-browser@16"
DEFAULT_EDGE_LIMIT = 100
MAX_EDGE_LIMIT = 400
MAX_LINEAGE_EDGE_LIMIT = 1_000
@ -277,10 +277,69 @@ class VisualizationIndexSnapshot:
"SELECT * FROM nodes WHERE node_id = ?", (node_id,)
).fetchone()
if root_row is None:
raise DocForgeError(
"missing_node",
"No node has the requested stable ID",
node_id=node_id,
logic_row = connection.execute(
"SELECT owner_node_id, logic_id, kind, label, source_anchor "
"FROM logic_nodes WHERE logic_id = ? "
"ORDER BY owner_node_id LIMIT 1",
(node_id,),
).fetchone()
if logic_row is None:
raise DocForgeError(
"missing_node",
"No node has the requested stable ID",
node_id=node_id,
)
owner_row = connection.execute(
"SELECT * FROM nodes WHERE node_id = ?",
(logic_row["owner_node_id"],),
).fetchone()
if owner_row is None:
raise DocForgeError(
"invalid_index",
"Logic projection owner is missing from the primary graph",
)
logic_nodes = connection.execute(
"SELECT logic_id, kind, label, source_anchor FROM logic_nodes "
"WHERE owner_node_id = ? ORDER BY logic_id",
(logic_row["owner_node_id"],),
).fetchall()
logic_edges = connection.execute(
"SELECT source_id, relation, target_id, label, ordinal "
"FROM logic_edges WHERE owner_node_id = ? "
"ORDER BY source_id, ordinal, relation, target_id",
(logic_row["owner_node_id"],),
).fetchall()
node = _logic_node_dict(
logic_row,
owner_row=owner_row,
owner_node_id=logic_row["owner_node_id"],
)
return self._result(
root=node_id,
depth=1,
edge_limit=limit,
truncated=False,
node=node,
nodes=[
_logic_node_dict(
row,
owner_row=owner_row,
owner_node_id=logic_row["owner_node_id"],
)
for row in logic_nodes
],
edges=[
{
"source_id": row["source_id"],
"relation": row["relation"],
"target_id": row["target_id"],
"label": row["label"],
"ordinal": row["ordinal"],
"reversed": False,
}
for row in logic_edges
],
snapshot=True,
)
visited = {node_id}
frontier = {node_id}
@ -342,6 +401,20 @@ class VisualizationIndexSnapshot:
"SELECT node_id, source_path, source_anchor FROM nodes WHERE node_id = ?",
(node_id,),
).fetchone()
if row is None:
row = connection.execute(
"""
SELECT logic.logic_id AS node_id,
owner.source_path AS source_path,
logic.source_anchor AS source_anchor
FROM logic_nodes AS logic
JOIN nodes AS owner ON owner.node_id = logic.owner_node_id
WHERE logic.logic_id = ?
ORDER BY logic.owner_node_id
LIMIT 1
""",
(node_id,),
).fetchone()
if row is None:
raise DocForgeError(
"missing_node",
@ -396,6 +469,75 @@ class VisualizationIndexSnapshot:
snapshot=True,
)
def logic(self, owner_node_id: str) -> dict[str, object]:
"""Return one lazy function-scoped control-flow projection."""
with self._connection() as connection:
owner_row = connection.execute(
"SELECT * FROM nodes WHERE node_id = ?",
(owner_node_id,),
).fetchone()
if owner_row is None:
raise DocForgeError(
"missing_node",
"No node has the requested stable ID",
node_id=owner_node_id,
)
owner = connection.execute(
"SELECT source_id FROM logic_owners WHERE owner_node_id = ?",
(owner_node_id,),
).fetchone()
if owner is None:
return self._result(
root=owner_node_id,
logic=True,
available=False,
owner=_node_dict(owner_row, include_content=False),
nodes=[],
edges=[],
snapshot=True,
)
node_rows = connection.execute(
"SELECT logic_id, kind, label, source_anchor FROM logic_nodes "
"WHERE owner_node_id = ? ORDER BY logic_id",
(owner_node_id,),
).fetchall()
edge_rows = connection.execute(
"SELECT source_id, relation, target_id, label, ordinal "
"FROM logic_edges WHERE owner_node_id = ? "
"ORDER BY source_id, ordinal, relation, target_id",
(owner_node_id,),
).fetchall()
nodes = [
_logic_node_dict(row, owner_row=owner_row, owner_node_id=owner_node_id)
for row in node_rows
]
entry = next(
(cast(str, node["node_id"]) for node in nodes if node["logic_kind"] == "entry"),
cast(str, nodes[0]["node_id"]) if nodes else owner_node_id,
)
edges = [
{
"source_id": row["source_id"],
"relation": row["relation"],
"target_id": row["target_id"],
"label": row["label"],
"ordinal": row["ordinal"],
"reversed": False,
}
for row in edge_rows
]
return self._result(
root=entry,
logic=True,
available=True,
source_id=owner["source_id"],
owner=_node_dict(owner_row, include_content=False),
nodes=nodes,
edges=edges,
snapshot=True,
)
def lineage(self, node_id: str, *, limit: int) -> dict[str, object]:
"""Return bounded semantic flow paths terminating at ``node_id``.
@ -941,6 +1083,9 @@ class VisualizationRunner:
elif parsed.path == f"{prefix}/api/web":
self._touch_lease()
payload = self._web(reader, params)
elif parsed.path == f"{prefix}/api/logic":
self._touch_lease()
payload = self._logic(reader, params)
else:
self._respond_error(
handler,
@ -1056,6 +1201,16 @@ class VisualizationRunner:
)
return reader.web(node_id, depth=depth, limit=limit)
@staticmethod
def _logic(
reader: VisualizationIndexSnapshot,
params: dict[str, list[str]],
) -> dict[str, object]:
owner_node_id = _one(params, "id").strip()
if not owner_node_id:
raise DocForgeError("missing_node", "One exact owner node ID is required")
return reader.logic(owner_node_id)
def _filter(
self,
reader: VisualizationIndexSnapshot,
@ -1546,6 +1701,29 @@ def _node_dict(row: sqlite3.Row, *, include_content: bool = True) -> dict[str, o
return result
def _logic_node_dict(
row: sqlite3.Row,
*,
owner_row: sqlite3.Row,
owner_node_id: str,
) -> dict[str, object]:
kind = cast(str, row["kind"])
return {
"node_id": row["logic_id"],
"title": row["label"],
"family": "logic",
"authority": "derived",
"status": "current",
"tags": ("logic", kind),
"summary": f"{kind.replace('_', ' ').title()} in {owner_row['title']}.",
"source_path": owner_row["source_path"],
"source_anchor": row["source_anchor"],
"content_hash": "",
"logic_kind": kind,
"logic_owner_id": owner_node_id,
}
def _facet_rows(connection: sqlite3.Connection, table: str, column: str) -> list[dict[str, object]]:
allowed = {
("nodes", "family"),