-
Notifications
You must be signed in to change notification settings - Fork 12
feat: wire impact analysis into audit health report #30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
a768ea7
a14f3e5
6af7714
75f427e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,6 +37,39 @@ func Analyze(ir *api.SupermodelIR, projectName string) *HealthReport { | |
| return r | ||
| } | ||
|
|
||
| // EnrichWithImpact adds impact analysis results to an existing HealthReport | ||
| // and re-scores status and recommendations. | ||
| func EnrichWithImpact(r *HealthReport, impact *api.ImpactResult) { | ||
| for i := range impact.Impacts { | ||
| imp := &impact.Impacts[i] | ||
| r.ImpactFiles = append(r.ImpactFiles, ImpactFile{ | ||
| Path: imp.Target.File, | ||
| RiskScore: imp.BlastRadius.RiskScore, | ||
| Direct: imp.BlastRadius.DirectDependents, | ||
| Transitive: imp.BlastRadius.TransitiveDependents, | ||
| Files: imp.BlastRadius.AffectedFiles, | ||
| }) | ||
| } | ||
| // Also pull in global critical files if the API returned them. | ||
| for i := range impact.GlobalMetrics.MostCriticalFiles { | ||
| cf := &impact.GlobalMetrics.MostCriticalFiles[i] | ||
| r.ImpactFiles = append(r.ImpactFiles, ImpactFile{ | ||
| Path: cf.File, | ||
| Direct: cf.DependentCount, | ||
| }) | ||
| } | ||
| // Cap to top 10 by direct dependents. | ||
| sort.Slice(r.ImpactFiles, func(i, j int) bool { | ||
| return r.ImpactFiles[i].Direct > r.ImpactFiles[j].Direct | ||
| }) | ||
| if len(r.ImpactFiles) > 10 { | ||
| r.ImpactFiles = r.ImpactFiles[:10] | ||
| } | ||
|
Comment on lines
+42
to
+70
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential duplicate files if the same path appears in both sources. So here's what's happening: you're pulling files from two places:
If the same file path appears in both lists, you'll end up with duplicates in Example scenario:
Consider deduplicating by path: 🔧 Suggested fix using a map func EnrichWithImpact(r *HealthReport, impact *api.ImpactResult) {
+ seen := make(map[string]bool)
for i := range impact.Impacts {
imp := &impact.Impacts[i]
+ if seen[imp.Target.File] {
+ continue
+ }
+ seen[imp.Target.File] = true
r.ImpactFiles = append(r.ImpactFiles, ImpactFile{
Path: imp.Target.File,
RiskScore: imp.BlastRadius.RiskScore,
Direct: imp.BlastRadius.DirectDependents,
Transitive: imp.BlastRadius.TransitiveDependents,
Files: imp.BlastRadius.AffectedFiles,
})
}
// Also pull in global critical files if the API returned them.
for i := range impact.GlobalMetrics.MostCriticalFiles {
cf := &impact.GlobalMetrics.MostCriticalFiles[i]
+ if seen[cf.File] {
+ continue
+ }
+ seen[cf.File] = true
r.ImpactFiles = append(r.ImpactFiles, ImpactFile{
Path: cf.File,
Direct: cf.DependentCount,
})
} |
||
| // Re-score and regenerate recommendations with impact data. | ||
| r.Status = scoreStatus(r) | ||
| r.Recommendations = generateRecommendations(r) | ||
| } | ||
|
|
||
| // ── helpers ─────────────────────────────────────────────────────────────────── | ||
|
|
||
| func buildExternalDeps(ir *api.SupermodelIR) []string { | ||
|
|
@@ -147,6 +180,11 @@ func scoreStatus(r *HealthReport) HealthStatus { | |
| if r.CircularDeps > 0 { | ||
| return StatusCritical | ||
| } | ||
| for i := range r.ImpactFiles { | ||
| if r.ImpactFiles[i].RiskScore == "critical" { | ||
| return StatusDegraded | ||
| } | ||
| } | ||
| for i := range r.Domains { | ||
| if len(r.Domains[i].IncomingDeps) >= 5 { | ||
| return StatusDegraded | ||
|
|
@@ -207,6 +245,16 @@ func generateRecommendations(r *HealthReport) []Recommendation { | |
| } | ||
| } | ||
|
|
||
| for i := range r.ImpactFiles { | ||
| f := &r.ImpactFiles[i] | ||
| if f.RiskScore == "critical" { | ||
| recs = append(recs, Recommendation{ | ||
| Priority: 1, | ||
| Message: fmt.Sprintf("File %q has critical blast radius (%d direct, %d transitive dependents) — changes here affect %d files.", f.Path, f.Direct, f.Transitive, f.Files), | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| sort.Slice(recs, func(i, j int) bool { return recs[i].Priority < recs[j].Priority }) | ||
| return recs | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: supermodeltools/cli
Length of output: 2016
Add a nil guard or clarify the contract.
The
EnrichWithImpactfunction accessesimpact.Impactsandimpact.GlobalMetricswithout checking ifimpactis nil first. If someone callsEnrichWithImpact(r, nil), it'll panic right away on that first for-loop.Either add a simple nil check at the start (like
if impact == nil { return }), or add a comment explaining that the caller must always pass a non-nilapi.ImpactResult. Also worth adding a test that documents the actual behavior—right now the test only covers the empty-but-valid case.🤖 Prompt for AI Agents