Add refactoring-cadence agentic workflow#25352
Conversation
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/d5e88768-c558-4d88-92f2-60d8e60e2e77 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Creates .github/workflows/refactoring-cadence.md and its compiled .lock.yml. The workflow: - Runs daily on weekdays (per @pelikhan's request) - Tracks 4 code health metrics: avg file length, cyclomatic complexity, file growth >20%, TODO/FIXME/HACK churn - Computes a 0–100 health score against a configurable threshold (default 70) - Auto-opens issues labeled refactoring + ai-generated when score drops - Stores baselines in cache-memory for trend comparison - Supports radon (Python), cargo-clippy (Rust), gocyclo (Go) - Skips if an open [refactoring-cadence] issue already exists Closes #25311 (plan issue)" Agent-Logs-Url: https://github.com/github/gh-aw/sessions/d5e88768-c558-4d88-92f2-60d8e60e2e77 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
|
@copilot this code base is Go and JavaScript |
- Default extensions changed from go,py,rs,js,ts,cjs → go,js,cjs - All file-scan patterns (avg length, file growth, TODO churn) now target *.go, *.js, *.cjs - Complexity analysis reordered: Go (gocyclo) and JavaScript (branch-count) are primary; Python (radon) and Rust (cargo-clippy) demoted to optional/secondary - JavaScript complexity section added with function-declaration and branch-point counting - Step 6 refactoring-plan tooling updated to use gocyclo (Go) and grep (JS) instead of radon/clippy - Updated description to call out Go and JavaScript as the primary targets Agent-Logs-Url: https://github.com/github/gh-aw/sessions/531f913b-fdbc-4826-a312-8b532b57124d Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Updated in 0a4ecfc. Go ( |
There was a problem hiding this comment.
Pull request overview
Adds a new scheduled agentic workflow (“Refactoring Cadence”) that tracks code-health metrics over time and opens a refactoring issue when the score degrades, plus accompanying compiled workflow artifacts.
Changes:
- Introduces the
refactoring-cadenceworkflow definition and its compiled lockfile. - Implements baseline persistence via cache-memory and issue creation via Safe Outputs when health drops below threshold.
- Updates
architecture-guardian.lock.ymlto run Copilot viacopilot_driver.cjs.
Show a summary per file
| File | Description |
|---|---|
| .github/workflows/refactoring-cadence.md | New agent workflow spec to compute health metrics, compare to baseline, and (conditionally) open an issue. |
| .github/workflows/refactoring-cadence.lock.yml | Generated compiled workflow implementing the new agent spec with caching, safe outputs, and scheduling. |
| .github/workflows/architecture-guardian.lock.yml | Regenerated compiled workflow to invoke Copilot through the standard driver script. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comments suppressed due to low confidence (1)
.github/workflows/refactoring-cadence.md:179
- The file-growth loop uses
while read f; do ...which will mangle paths containing backslashes and can trim leading/trailing whitespace. Usewhile IFS= read -r f(and ideally NUL-delimit via-print0) to keep file paths intact.
find . -type f \( -name "*.go" -o -name "*.js" -o -name "*.cjs" \) \
! -path "*/vendor/*" ! -path "*/node_modules/*" ! -path "*/.git/*" \
! -name "*_test.go" ! -name "*.lock.yml" ! -name "*.pb.go" \
| sort | while read f; do
lines=$(wc -l < "$f" 2>/dev/null || echo 0)
echo "$f $lines"
done
- Files reviewed: 3/3 changed files
- Comments generated: 5
| find . -type f \( -name "*.go" -o -name "*.js" -o -name "*.cjs" \) \ | ||
| ! -path "*/vendor/*" ! -path "*/node_modules/*" ! -path "*/.git/*" \ | ||
| ! -name "*_test.go" ! -name "*.lock.yml" ! -name "*.pb.go" \ | ||
| | xargs wc -l 2>/dev/null | awk '$2 != "total" {sum+=$1; count++} END {if(count>0) print sum/count; else print 0}' |
There was a problem hiding this comment.
Metric 1 uses find ... | xargs wc -l, which will mis-handle file paths containing whitespace/newlines and can silently skew avg_file_length/file_count. Prefer find ... -print0 | xargs -0 wc -l or find ... -exec wc -l {} + to make the metric path-safe.
This issue also appears on line 173 of the same file.
| | xargs wc -l 2>/dev/null | awk '$2 != "total" {sum+=$1; count++} END {if(count>0) print sum/count; else print 0}' | |
| -exec wc -l {} + 2>/dev/null | awk '$2 != "total" {sum+=$1; count++} END {if(count>0) print sum/count; else print 0}' |
| # Compute average cyclomatic complexity across all Go functions | ||
| GO_FILES=$(find . -name "*.go" ! -path "*/vendor/*" ! -name "*_test.go" ! -path "*/.git/*" 2>/dev/null) | ||
| if [ -n "$GO_FILES" ] && command -v gocyclo >/dev/null 2>&1; then | ||
| GO_AVG=$(gocyclo -avg . 2>/dev/null | grep "^Average" | awk '{print $NF}') |
There was a problem hiding this comment.
GO_FILES is filtered to exclude vendor/ and *_test.go, but the gocyclo invocation runs on . (which may include vendor/tests/generated code). This makes the complexity metric inconsistent with the documented exclusions; pass only the filtered file set (or run gocyclo on filtered packages/paths) so avg_complexity reflects the intended scope.
| GO_AVG=$(gocyclo -avg . 2>/dev/null | grep "^Average" | awk '{print $NF}') | |
| GO_AVG=$(echo "$GO_FILES" | xargs gocyclo -avg 2>/dev/null | grep "^Average" | awk '{print $NF}') |
| FUNC_COUNT=$(echo "$JS_FILES" | xargs grep -cE \ | ||
| "(^|\s)(function\s+\w+|const\s+\w+\s*=\s*(\([^)]*\)|[a-z_]\w*)\s*=>|async\s+function\s+\w+)" \ | ||
| 2>/dev/null | awk -F: '{sum+=$2} END {print sum+0}') | ||
| # Count branch points: if, for, while, switch, &&, ||, ?? |
There was a problem hiding this comment.
The JS complexity comment says it counts branch points including &&, ||, and ??, but the regex only counts if/for/while/switch statements. Either update the regex to match the described operators or adjust the comment so the metric definition matches the implementation.
| # Count branch points: if, for, while, switch, &&, ||, ?? | |
| # Count branch statements: if, for, while, switch |
| pip install radon --quiet 2>/dev/null || true | ||
| echo "$PY_FILES" | xargs radon cc -s -a 2>/dev/null \ | ||
| | grep "Average complexity:" | awk '{sum+=$NF; count++} END {if(count>0) print "py:" sum/count; else print "py:0"}' |
There was a problem hiding this comment.
The Python path installs radon with pip install (no venv) and then unconditionally runs radon via xargs. On GitHub-hosted runners, pip install often fails due to an externally-managed environment, and the workflow doesn't verify radon is available before use. Consider using a venv (as done in .github/workflows/shared/python-dataviz.md) and/or guarding the radon call with command -v radon, plus adding the appropriate network.allowed entry if this workflow is expected to fetch packages.
| pip install radon --quiet 2>/dev/null || true | |
| echo "$PY_FILES" | xargs radon cc -s -a 2>/dev/null \ | |
| | grep "Average complexity:" | awk '{sum+=$NF; count++} END {if(count>0) print "py:" sum/count; else print "py:0"}' | |
| if python3 -m venv .radon-venv 2>/dev/null; then | |
| . .radon-venv/bin/activate | |
| if python -m pip install --quiet radon 2>/dev/null && command -v radon >/dev/null 2>&1; then | |
| echo "$PY_FILES" | xargs radon cc -s -a 2>/dev/null \ | |
| | grep "Average complexity:" | awk '{sum+=$NF; count++} END {if(count>0) print "py:" sum/count; else print "py:0"}' | |
| else | |
| echo "py:0" | |
| fi | |
| else | |
| echo "py:0" | |
| fi |
| # Count all TODO/FIXME/HACK comments in Go and JavaScript source files | ||
| find . -type f \( -name "*.go" -o -name "*.js" -o -name "*.cjs" \) \ | ||
| ! -path "*/vendor/*" ! -path "*/node_modules/*" ! -path "*/.git/*" \ | ||
| ! -name "*.lock.yml" \ |
There was a problem hiding this comment.
Metric 4 claims the workflow excludes *_test.go and generated files, but this find command does not exclude *_test.go (or *.pb.go). That makes TODO churn inconsistent with the other metrics and can cause churn to change due to test/generated updates. Align the exclusions here with Metrics 1/3.
| ! -name "*.lock.yml" \ | |
| ! -name "*_test.go" ! -name "*.lock.yml" ! -name "*.pb.go" \ |
Creates a daily weekday workflow that tracks repository code health over time and auto-opens a GitHub issue when quality degrades — a forcing function for continuous refactoring.
Workflow:
refactoring-cadenceTrigger: Daily on weekdays (scattered cron). Skips if an open
[refactoring-cadence]issue already exists.4 health metrics → 0–100 score:
wc -lgocyclo(Go, primary), branch-count grep (JavaScript, primary),radon(Python, optional),cargo clippy(Rust, optional)grepPrimary languages: Go and JavaScript/CommonJS — all file scans target
*.go,*.js, and*.cjsby default. Python and Rust analysis runs automatically if those files are present, but are not required.When score drops below threshold (default: 70): opens issue
[refactoring-cadence] 🔧 Refactoring Required: Code Health Alertlabeledrefactoring+ai-generated, containing per-file degradation details, an AI-generated prioritized refactoring plan, and an actionable checklist.Baseline persistence: metrics stored as JSON in
cache-memorybetween runs. First run is baseline-only — no issue created.Configurable via
.refactoring-cadence.ymlat repo root (threshold, max avg file length, max avg complexity, growth %, TODO churn limit). Default extensions:go,js,cjs.