fix: use undirected reachability for reviewer affected-objectives tracing
The directed graph has mixed edge directions along the path from agent/formula to objective (agent→formula→label←issue→objective), so descendants() never reaches objectives. Use undirected connected components for reachability instead. Also fix closed-issues query to use forge_get (bounded at 50) instead of forge_get_all (unbounded). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3a7b95046d
commit
4d429b274a
1 changed files with 23 additions and 21 deletions
|
|
@ -248,8 +248,8 @@ def parse_evidence(G, root):
|
||||||
def parse_forge_issues(G, token):
|
def parse_forge_issues(G, token):
|
||||||
"""Fetch issues from the Forge API and add nodes/edges."""
|
"""Fetch issues from the Forge API and add nodes/edges."""
|
||||||
issues = forge_get_all("/issues?state=open&type=issues", token)
|
issues = forge_get_all("/issues?state=open&type=issues", token)
|
||||||
issues += forge_get_all("/issues?state=closed&type=issues&sort=updated"
|
issues += forge_get("/issues?state=closed&type=issues&sort=updated"
|
||||||
"&direction=desc&limit=50", token)
|
"&direction=desc&limit=50", token)
|
||||||
seen = set()
|
seen = set()
|
||||||
for issue in issues:
|
for issue in issues:
|
||||||
num = issue.get("number")
|
num = issue.get("number")
|
||||||
|
|
@ -430,19 +430,15 @@ def filter_for_changed_files(report, G, changed_files, root):
|
||||||
affected_prereqs = set()
|
affected_prereqs = set()
|
||||||
alerts = []
|
alerts = []
|
||||||
|
|
||||||
|
# Collect changed-file graph nodes to trace from
|
||||||
|
changed_nodes = set()
|
||||||
for fpath in changed_files:
|
for fpath in changed_files:
|
||||||
# Check if the file relates to a formula
|
# Check if the file relates to a formula
|
||||||
if fpath.startswith("formulas/"):
|
if fpath.startswith("formulas/"):
|
||||||
fname = os.path.basename(fpath).replace(".toml", "")
|
fname = os.path.basename(fpath).replace(".toml", "")
|
||||||
for node in G.nodes():
|
for node in G.nodes():
|
||||||
if node.startswith("formula:") and fname in node:
|
if node.startswith("formula:") and fname in node:
|
||||||
# Trace to objectives
|
changed_nodes.add(node)
|
||||||
try:
|
|
||||||
for desc in nx.descendants(G, node):
|
|
||||||
if G.nodes.get(desc, {}).get("type") == "objective":
|
|
||||||
affected_objectives.add(desc)
|
|
||||||
except nx.NetworkXError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Check if file is in an agent directory
|
# Check if file is in an agent directory
|
||||||
for agent in ["dev", "review", "gardener", "predictor", "planner",
|
for agent in ["dev", "review", "gardener", "predictor", "planner",
|
||||||
|
|
@ -450,23 +446,29 @@ def filter_for_changed_files(report, G, changed_files, root):
|
||||||
if fpath.startswith(f"{agent}/"):
|
if fpath.startswith(f"{agent}/"):
|
||||||
agent_id = f"agent:{agent}"
|
agent_id = f"agent:{agent}"
|
||||||
if G.has_node(agent_id):
|
if G.has_node(agent_id):
|
||||||
try:
|
changed_nodes.add(agent_id)
|
||||||
for desc in nx.descendants(G, agent_id):
|
|
||||||
if G.nodes.get(desc, {}).get("type") == "objective":
|
|
||||||
affected_objectives.add(desc)
|
|
||||||
except nx.NetworkXError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Check if file is evidence
|
# Check if file is evidence
|
||||||
if fpath.startswith("evidence/"):
|
if fpath.startswith("evidence/"):
|
||||||
for node in G.nodes():
|
for node in G.nodes():
|
||||||
if node.startswith("evidence:") and _slug(fpath) in _slug(node):
|
if node.startswith("evidence:") and _slug(fpath) in _slug(node):
|
||||||
try:
|
changed_nodes.add(node)
|
||||||
for desc in nx.descendants(G, node):
|
|
||||||
if G.nodes.get(desc, {}).get("type") == "prerequisite":
|
# The path from agent/formula to objective crosses edges with mixed
|
||||||
affected_prereqs.add(desc)
|
# directions (agent→formula→label←issue→objective), so use the
|
||||||
except nx.NetworkXError:
|
# undirected view to check reachability.
|
||||||
pass
|
undirected = G.to_undirected()
|
||||||
|
for changed in changed_nodes:
|
||||||
|
try:
|
||||||
|
reachable = nx.node_connected_component(undirected, changed)
|
||||||
|
except (nx.NetworkXError, KeyError):
|
||||||
|
continue
|
||||||
|
for r in reachable:
|
||||||
|
ntype = G.nodes.get(r, {}).get("type")
|
||||||
|
if ntype == "objective":
|
||||||
|
affected_objectives.add(r)
|
||||||
|
elif ntype == "prerequisite":
|
||||||
|
affected_prereqs.add(r)
|
||||||
|
|
||||||
# Check for DONE prerequisites affected by changes
|
# Check for DONE prerequisites affected by changes
|
||||||
for prereq in affected_prereqs:
|
for prereq in affected_prereqs:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue