ci: add graphify structural impact analysis to PR review and structure audit#567
Conversation
…e 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 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)
SummaryPR #567 adds a graphify-based AST structural-impact tool and wires it into two CI workflows:
Retroactive validation against 3 merged PRs is cited in the description; signal correlation with actual critical findings looks plausible. FindingsSecurity — correctly handled, one residual concernThe critical security story (tool must run from trusted base-branch code, not PR code) is executed well:
One residual concern in CHANGED_PY=$(gh pr diff "$PR_NUMBER" --name-only | grep '\.py$' || true)
...
--changed-files $CHANGED_PY \
mapfile -t CHANGED_PY < <(gh pr diff "$PR_NUMBER" --name-only | grep '\.py$' || true)
...
--changed-files "${CHANGED_PY[@]}"This also fixes a latent bug: if Correctness
Style / project conventions
Test coverage
Performance
Minor / nits
VerdictApprove with minor follow-ups. The core design — trusted-path execution of a pure-AST tool with Recommended before merge:
Recommended after merge: No blocking issues; security model is correct; retroactive validation supports the signal claim. |
Greptile SummaryThis PR adds a
|
| Filename | Overview |
|---|---|
| .agents/tools/structural_impact.py | New 411-line analysis tool; builds a full-codebase directed AST graph and reports risk level, god nodes, import violations, and cross-package deps. Previously-flagged issues (ID stability, dedup truncation, risk-reason accuracy) addressed in prior commits. Logic is sound. |
| .github/workflows/agentic-ci-pr-review.yml | Adds structural impact pre-computation step; correctly runs the tool from the base-branch sparse checkout so fork PRs cannot inject code. Version pin, continue-on-error, and --repo-root flag all look correct. |
| .github/workflows/agentic-ci-daily.yml | Adds conditional graphify install for the structure suite and expands the actions/cache path to persist graphify-out/ for week-over-week graph diffs. Pinned to 0.4.23. Straightforward. |
| .agents/recipes/pr-review/recipe.md | Adds pre-read of the structural impact file and appends the section to the review output before the Verdict. Clean integration with the existing recipe flow. |
| .agents/recipes/structure/recipe.md | Adds Section 4 with graphify instructions, baseline tracking, and week-over-week diff guidance. Summary section extended with god-node and cross-package-edge metrics. No issues. |
| .agents/skills/review-code/SKILL.md | Inserts Step 3.5 to read the pre-computed structural impact file (if present) and calibrate review depth accordingly. Graceful skip when the file is absent. |
| .gitignore | Adds graphify-out/ to gitignore so the CI-generated graph cache is never committed. Correct. |
Sequence Diagram
sequenceDiagram
participant GH as GitHub Actions
participant BC as Base-Branch Checkout (base-agents/)
participant WS as PR Workspace (github.workspace)
participant TMP as /tmp
GH->>GH: Sparse-checkout base branch (.agents/recipes + .agents/tools)
GH->>GH: gh pr diff --name-only | grep .py > changed-py.txt
GH->>BC: python base-agents/.agents/tools/structural_impact.py --repo-root workspace --changed-files ...
BC->>WS: _collect_source_files() reads all *.py via --repo-root
BC->>BC: extract() -> build_from_json() -> cluster() -> god_nodes()
BC->>BC: changed_node_ids derived from graph (source_file match)
BC->>TMP: Write structural-impact-{pr}.md (risk + findings)
GH->>GH: Run review agent
GH->>TMP: Agent reads structural-impact-{pr}.md (Step 3.5)
GH->>TMP: Agent appends Structural Impact section to review-{pr}.md
Reviews (6): Last reviewed commit: "Merge branch 'main' into andreatgretel/f..." | Re-trigger Greptile
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.
…filenames Replace unquoted $CHANGED_PY word-split with mapfile + array expansion to prevent glob expansion and correctly handle filenames containing spaces or special characters.
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.
…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.
|
Thanks for putting this together, @andreatgretel — the retroactive validation against three merged PRs is a really nice way to make the case for the signal quality. SummaryAdds a graphify-based AST analyzer at FindingsWarnings — Worth addressing
from dataclasses import dataclass
from typing import Any
@dataclass(frozen=True, slots=True)
class _Analysis:
graph: Any
communities: dict[int, list[str]]
cohesion: dict[str, Any]
god_nodes: list[dict[str, Any]]
def _build_graph(files: list[Path]) -> _Analysis: ...
def _cross_package_edges(G: Any, node_ids: set[str] | None = None) -> ...: ...Call sites become
Suggestions — Take it or leave it
- name: Install graphify
if: matrix.suite == 'structure'
run: |
python -m venv /tmp/graphify-venv
/tmp/graphify-venv/bin/python -m pip install graphifyy==0.4.23 2>&1 | tail -3Then invoke
What Looks Good
VerdictShip it (with nits) — Only Warnings around type annotations and the hardcoded package list, plus a handful of Suggestions. Nothing blocking, but the type annotations are worth addressing in this PR since they set the convention for the new file. Everything else can land as-is or in a follow-up. This review was generated by an AI assistant. |
…w 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.
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.
|
Thanks @nabinchha, this was a careful review and the type-annotation push set the right precedent for the file. Almost everything went into two follow-up commits on the branch: 508f119 — STYLEGUIDE-cited fixes + daily venv isolation
07b0e74 — unknown-package hedge
Deferred to #601 — unit tests for |
|
Thanks for the careful work here, @andreatgretel — the retroactive validation table on PRs #502/#545/#557 is a really nice touch and gives me good confidence that the signal is worth the complexity. SummaryThis PR adds a graphify-based AST analysis tool ( I had a few thoughts on top of what's already been resolved. FindingsWarnings — Worth addressing
Suggestions — Take it or leave it
What Looks Good
VerdictShip it (with nits) — once the argparse-injection guard is in (the only finding I'd actually want addressed before merge), everything else is take-it-or-leave-it. The package-naming source-of-truth point is worth tackling, but it's not blocking — adding a new package is rare, and the unknown-package warning gives us a soft tripwire today. This review was generated by an AI assistant. |
📋 Summary
Add a graphify-based AST analysis tool that builds a
directed graph of the codebase (~6s, zero LLM cost) to surface architectural risk before the
review agent starts. Integrates into both the PR review workflow (pre-computed context for the
agent) and the Wednesday structure audit (week-over-week graph diff).
Hat tip to @nabinchha for suggesting graphify as the right tool for this.
🔗 Related Issue
Closes #472 (Phase 4 — structural analysis)
🔄 Changes
✨ Added
structural_impact.py—standalone analysis tool with two modes:
--changed-files(PR review): extracts changed AST entities against the full codebase graph,reports risk level (LOW/MEDIUM/HIGH), god nodes affected, import direction violations, and
cross-package dependencies
--full(structure audit): god node rankings, cross-package edge summary table, importviolation detection, and graph diff against the previous week's cached graph
review-codeskill —agent reads the pre-computed structural impact to calibrate review depth (HIGH risk = extra
scrutiny on blast radius and backward compatibility)
graphify-out/added to.gitignore(9MB graph cache is CI-only, persisted via actions/cache)🔧 Changed
agentic-ci-pr-review.yml—new step installs graphifyy in a lightweight temp venv and runs structural analysis before the
agent starts. Tool is read from the base-branch checkout (
base-agents/) so fork PRs cannotinject code that runs with
GH_TOKENin scope. Base-branch checkout expanded from.agents/recipesto also include.agents/toolsagentic-ci-daily.yml—conditional graphifyy install for the structure suite;
graphify-out/added to the cache pathfor week-over-week graph diffs
pr-reviewrecipe —agent reads
/tmp/structural-impact-{{pr_number}}.mdand appends findings to the reviewstructurerecipe —new Section 4 with graphify instructions, baseline tracking, and week-over-week diff guidance
🔍 Attention Areas
agentic-ci-pr-review.yml—the security model:
structural_impact.pyruns frombase-agents/(base-branch sparsecheckout), not from the PR's files, to prevent fork PRs from injecting code that executes
with secrets in scope. The
--repo-rootflag ensures the tool still analyzes the PR'ssource code correctly despite living in a different checkout
structural_impact.py—new 361-line tool; pure AST analysis, no DD imports at runtime. Depends on
graphifyy(tree-sitter based) andnetworkx(transitive)📊 Retroactive validation on 3 merged PRs
Ran graphify retroactively against PRs #502, #545, and #557 to validate the signal quality:
DatasetBuilder(88 deps) +ExecutionGraph(89 deps) flagged as highest-riskconfig.__getattr__()calls into engineIn all 3 cases, the most critical review findings correlated with the highest-connectivity
entities in the graphify report. The risk level (LOW/MEDIUM/HIGH) matched actual review
effort needed.
🧪 Testing
structural_impact.pysmoke-tested in both modes (--changed-files,--full)against the live DD codebase
--repo-rootflag tested from external checkout directory (simulates CIbase-agents/path)graphifyy==0.4.23✅ Checklist
.agents/tooling, not package architecture