Skip to content

Commit b502dd3

Browse files
ci: add graphify structural impact analysis to PR review and structure audit (#567)
* ci: add graphify structural impact analysis to PR review and structure audit Add a graphify-based AST analysis tool that builds a directed graph of the codebase (~2s, no LLM calls) to detect architectural impact. Integrates into both the PR review workflow (pre-computed before claude runs) and the Wednesday structure audit (with week-over-week diff). PR review: extracts changed files against the full codebase graph, reports risk level (LOW/MEDIUM/HIGH), god nodes affected, import direction violations, and cross-package dependencies. Output saved to /tmp and read by the review agent. Structure audit: produces god node rankings, cross-package edge summary table, import violation detection, and graph diff against previous week's cached graph. Baselines saved for runner memory trend tracking. * fix: harden graphify integration - security, correctness, and CI weight - Fix KeyError: god_nodes() returns 'degree' not 'edges' (3 call sites) - Fix deduped vs raw violation count inconsistency in baselines.json - Security: run structural_impact.py from base-branch checkout so fork PRs cannot inject code that executes with GH_TOKEN in scope - Add --repo-root flag so the tool resolves package paths correctly when invoked from a different checkout directory - Replace make install-dev + .venv with lightweight /tmp/graphify-venv (only graphifyy needed, saves ~2min CI per PR review) - Add graphify-out/ to .gitignore (9MB graph cache is CI-only) * fix: pin graphifyy version and fix dedup truncation Pin graphifyy==0.4.23 in both CI workflows to prevent breaking changes from unpinned installs. Fix _dedup() label truncation at 30 chars that could merge distinct entities sharing a common prefix. * fix(ci): use array expansion for changed-files arg to handle special filenames Replace unquoted $CHANGED_PY word-split with mapfile + array expansion to prevent glob expansion and correctly handle filenames containing spaces or special characters. * fix: derive changed nodes from graph and improve MEDIUM risk reason Derive changed_node_ids from the already-built graph by matching source_file paths instead of running a separate extraction pass. Removes implicit dependency on graphify ID stability across independent extractions. Fix MEDIUM risk reason to reflect the actual trigger (cluster spread vs high-connectivity entity) instead of always reporting cluster count. * fix: address Codex review findings - security, edge coverage, dedup, stale artifacts Split the workflow step to isolate GH_TOKEN from graphifyy execution, preventing a compromised package release from exfiltrating write-scoped tokens. Scan both edge directions in _cross_package_edges so inbound dependents and violations where the changed node is the target are visible. Detect deleted files and report them as a risk signal. Include relation type in dedup key so distinct edge types between the same labels are not collapsed. Clean stale /tmp artifacts before running analysis to prevent reruns from reading old reports. * fix: address review feedback - type annotations, hoist imports, narrow except, isolate daily graphify - structural_impact.py: - replace bare _build_graph dict return with frozen _Analysis dataclass - add G: Any annotation on _cross_package_edges (STYLEGUIDE: all params typed) - hoist `from graphify.export import to_json` and `from networkx.readwrite import json_graph` to module top (no perf justification for deferred import) - narrow `except Exception` in graph-diff fallback to (JSONDecodeError, KeyError, TypeError, OSError) - agentic-ci-daily.yml: install graphifyy into /tmp/graphify-venv instead of the project .venv, matching agentic-ci-pr-review.yml. Keeps graphify's transitives (networkx) out of the project venv permanently. - structure/recipe.md: invoke the tool via /tmp/graphify-venv/bin/python to match the workflow change. * feat(ci): warn when changed files touch unknown packages A new package under packages/ that isn't in _PACKAGE_SUBDIRS is silently absent from the graph - the analyzer would falsely report LOW risk with 0 entities. Add a _Note line in the changed-files report when any changed or deleted file lives under packages/<unknown>/, so the failure mode the analyzer is supposed to surface isn't itself silent. _KNOWN_PACKAGE_DIRS is derived from _PACKAGE_SUBDIRS so future additions stay in sync without a second source of truth.
1 parent 98715dc commit b502dd3

7 files changed

Lines changed: 533 additions & 14 deletions

File tree

.agents/recipes/pr-review/recipe.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,17 @@ Review pull request #{{pr_number}} using the `review-code` skill.
1717

1818
## Instructions
1919

20-
1. Run `/review-code {{pr_number}}`
21-
2. The skill writes the review to `/tmp/review-{{pr_number}}.md`. If it does
20+
1. If `/tmp/structural-impact-{{pr_number}}.md` exists, read it. This contains
21+
a pre-computed structural impact analysis (risk level, god nodes affected,
22+
import direction violations, cross-package dependencies). Use it to inform
23+
your review - high-risk PRs warrant extra scrutiny on blast radius and
24+
backward compatibility.
25+
2. Run `/review-code {{pr_number}}`
26+
3. The skill writes the review to `/tmp/review-{{pr_number}}.md`. If it does
2227
not, save the review output there yourself.
23-
3. Before finishing, read `/tmp/review-{{pr_number}}.md` and verify it contains
28+
4. If the structural impact analysis exists, append its content to the review
29+
file (before the Verdict section) under a `### Structural Impact` heading.
30+
5. Before finishing, read `/tmp/review-{{pr_number}}.md` and verify it contains
2431
a valid review (Summary, Findings, Verdict sections). If the file is empty
2532
or malformed, write a brief "Review could not be completed" note to the file
2633
instead.

.agents/recipes/structure/recipe.md

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,38 @@ done
111111
This enables modern type syntax (`list[str]` instead of `List[str]`,
112112
`str | None` instead of `Optional[str]`) and defers annotation evaluation.
113113

114-
### 4. Dead exports
114+
### 4. Graphify structural analysis
115+
116+
Run the graphify-based structural analysis tool. This builds a directed AST
117+
graph of the entire codebase (~2s) and produces god node rankings,
118+
cross-package edge counts, and import direction violations that grep-based
119+
checks miss (e.g., inferred cross-file dependencies from class-level import
120+
resolution).
121+
122+
```bash
123+
PREV_GRAPH=""
124+
if [ -f "graphify-out/graph.json" ]; then
125+
PREV_GRAPH="--previous-graph graphify-out/graph.json"
126+
fi
127+
/tmp/graphify-venv/bin/python .agents/tools/structural_impact.py --full $PREV_GRAPH \
128+
--output /tmp/graphify-structure.md
129+
```
130+
131+
Read `/tmp/graphify-structure.md` and incorporate relevant findings:
132+
133+
- **God nodes**: track the top 10 most-connected entities. Compare against
134+
the previous baseline in runner memory. A god node gaining 10+ connections
135+
since last week signals growing coupling.
136+
- **Cross-package edge summary**: the table shows edge counts per direction.
137+
All edges should flow interface -> engine -> config. Any VIOLATION row is a
138+
finding for the import boundary section.
139+
- **Changes since last run**: new/removed entities and relationships since
140+
the previous audit. Flag any unexpected removals of god nodes.
141+
142+
After the audit, update `baselines` in runner memory with the current god
143+
node list and cross-package edge counts from `graphify-out/baselines.json`.
144+
145+
### 5. Dead exports
115146

116147
Find symbols in `__all__` that nothing outside their module references:
117148

@@ -161,12 +192,18 @@ Write the report to `/tmp/audit-{{suite}}.md`:
161192
|---------|--------|--------|----------|
162193
| ... | ... | ... | No external imports found |
163194

195+
### Structural graph analysis (graphify)
196+
197+
(Paste the content of /tmp/graphify-structure.md here)
198+
164199
### Summary
165200

166201
- N import boundary violations (M new since last run)
167202
- N lazy import violations (M new)
168203
- N files missing future annotations (M new)
169204
- N potentially dead exports (M new)
205+
- God nodes: top entity has N connections (delta: +/-M from last run)
206+
- Cross-package edges: N total (M violations)
170207
```
171208

172209
If no findings in any category, write `NO_FINDINGS` on the first line instead.

.agents/skills/review-code/SKILL.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,33 @@ Before diving into details, build a mental model:
112112
5. **Note cross-cutting concerns** (e.g., a rename that touches many files vs. substantive logic changes)
113113
6. **Check existing feedback** (PR mode): inspect both inline comments (Step 1, item 5) and PR-level review bodies (Step 1, item 5b) so you don't duplicate feedback already given
114114

115+
## Step 3.5: Structural Impact (if available)
116+
117+
Check for a pre-computed structural impact analysis at
118+
`/tmp/structural-impact-<pr-or-branch>.md`. This file is produced by
119+
`graphify` AST extraction and contains:
120+
121+
- **Risk level** (LOW/MEDIUM/HIGH) based on god nodes touched, import
122+
violations, and cluster spread
123+
- **Core abstractions modified** - the most-connected entities in the
124+
codebase (high blast radius if changed)
125+
- **Import direction violations** - cross-package edges that violate the
126+
layering rule (interface -> engine -> config)
127+
- **High-connectivity changes** - entities with many dependents
128+
- **Cross-package dependencies** - edges crossing package boundaries
129+
130+
If the file exists, read it and use it to calibrate your review:
131+
132+
- **HIGH risk**: apply extra scrutiny in Pass 2 (Design & Architecture).
133+
Verify backward compatibility for god nodes. Check that cross-package
134+
changes don't break existing callers.
135+
- **Import violations**: flag them as Warnings in the review if they
136+
represent real dependency direction issues (not just inferred edges).
137+
- **LOW risk**: the structural analysis confirms a localized change. You
138+
can focus more on correctness (Pass 1) and less on architecture.
139+
140+
If the file does not exist (e.g. local branch review), skip this step.
141+
115142
## Step 4: Review Each Changed File (Multi-Pass)
116143

117144
Perform **at least 2-3 passes** over the changed files. Each pass has a different focus — this catches issues that a single read-through would miss.

0 commit comments

Comments
 (0)