Skip to content

Commit 1066a2f

Browse files
feat: add cyclomatic complexity reducer agentic workflow (#298)
* feat: add cyclomatic complexity reducer agentic workflow Daily scheduled GitHub Agentic Workflow that: 1. Runs Clippy cognitive complexity analysis in JSON mode 2. Ranks functions by complexity score 3. Refactors the highest-complexity function (extract helpers, flatten nesting, simplify logic) 4. Verifies tests pass and complexity is reduced 5. Submits a PR with the changes 6. Uses cache-memory to track progress across runs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: use read-only permissions for complexity reducer workflow gh-aw strict mode (default) refuses write permissions — writes happen through the safe-outputs system (create-pull-request). Align with the pattern used by all other workflows in this repo. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * regenerate with newer gh-aw --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 75b5f57 commit 1066a2f

11 files changed

Lines changed: 2299 additions & 563 deletions

.github/aw/actions-lock.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
"version": "v9.0.0",
1111
"sha": "d746ffe35508b1917358783b479e04febd2b8f71"
1212
},
13-
"github/gh-aw-actions/setup@v0.68.1": {
13+
"github/gh-aw-actions/setup@v0.68.3": {
1414
"repo": "github/gh-aw-actions/setup",
15-
"version": "v0.68.1",
16-
"sha": "2fe53acc038ba01c3bbdc767d4b25df31ca5bdfc"
15+
"version": "v0.68.3",
16+
"sha": "ba90f2186d7ad780ec640f364005fa24e797b360"
1717
},
1818
"github/gh-aw/actions/setup@v0.68.1": {
1919
"repo": "github/gh-aw/actions/setup",

.github/workflows/cyclomatic-complexity-reducer.lock.yml

Lines changed: 1303 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
---
2+
on:
3+
schedule: daily
4+
description: Identifies and refactors functions with high cyclomatic complexity using Clippy analysis
5+
permissions:
6+
contents: read
7+
pull-requests: read
8+
issues: read
9+
tools:
10+
github:
11+
toolsets: [default]
12+
cache-memory: true
13+
network:
14+
allowed: [defaults, rust]
15+
safe-outputs:
16+
create-pull-request:
17+
max: 1
18+
---
19+
20+
# Cyclomatic Complexity Reducer
21+
22+
You are a senior Rust engineer focused on code maintainability. Your job is to find the most complex functions in this codebase using Clippy's cognitive complexity lint and refactor them to be simpler, more readable, and easier to test — without changing behaviour.
23+
24+
## Step 1: Check Previous Runs
25+
26+
Read cache-memory to avoid re-processing functions that were already refactored or deliberately skipped:
27+
28+
```bash
29+
cat /tmp/gh-aw/cache-memory/complexity-state.json 2>/dev/null || echo "{}"
30+
```
31+
32+
## Step 2: Run Clippy Complexity Analysis
33+
34+
Run Clippy in JSON mode with the cognitive complexity lint explicitly enabled and configured with a low threshold to surface candidates:
35+
36+
```bash
37+
cargo clippy --all-targets --all-features --message-format=json -- \
38+
-W clippy::cognitive_complexity 2>/dev/null \
39+
| jq -c 'select(.reason == "compiler-message")
40+
| select(.message.code.code == "clippy::cognitive_complexity")
41+
| {
42+
file: .message.spans[0].file_name,
43+
start: .message.spans[0].line_start,
44+
end: .message.spans[0].line_end,
45+
text: .message.message
46+
}' \
47+
| sort -t'"' -k8 -rn
48+
```
49+
50+
If no results appear, try lowering the threshold:
51+
52+
```bash
53+
cargo clippy --all-targets --all-features --message-format=json -- \
54+
-W clippy::cognitive_complexity \
55+
--cfg 'clippy' 2>/dev/null \
56+
| jq -c 'select(.reason == "compiler-message")
57+
| select(.message.code.code == "clippy::cognitive_complexity")' \
58+
| head -20
59+
```
60+
61+
If Clippy doesn't flag any functions at the default threshold, that's fine — fall back to a manual scan. Look for functions with deeply nested control flow:
62+
63+
```bash
64+
# Find functions with many levels of nesting (heuristic)
65+
grep -rn 'fn ' src/ --include='*.rs' | head -50
66+
```
67+
68+
Then read the longest/most complex-looking functions and assess them manually.
69+
70+
## Step 3: Rank and Select Target
71+
72+
From the results, pick the **single function with the highest reported complexity**. If cache-memory shows it was already processed, move to the next one.
73+
74+
Read the full function to understand its structure:
75+
76+
```bash
77+
# Example — adjust file/lines from Step 2 output
78+
sed -n '<start>,<end>p' <file>
79+
```
80+
81+
Also read surrounding context (the impl block, callers, tests) so you understand the function's contract.
82+
83+
## Step 4: Plan the Refactor
84+
85+
Before changing code, plan a strategy. Common approaches, in order of preference:
86+
87+
1. **Extract helper functions** — break logically distinct blocks into well-named functions. This is almost always the right first move.
88+
2. **Flatten nested control flow** — use early returns, `let-else`, or guard clauses to reduce nesting depth.
89+
3. **Simplify boolean logic** — combine conditions, use `matches!()`, eliminate double negations.
90+
4. **Replace branching with data** — use lookup tables, iterators, or `Option`/`Result` combinators instead of long match arms.
91+
5. **Split into modules** — if the function is doing too many things, the enclosing module may need restructuring.
92+
93+
**Rules:**
94+
- Do NOT change public API signatures.
95+
- Do NOT change observable behaviour — the existing tests must continue to pass.
96+
- Preserve all comments that are still relevant.
97+
- Name extracted functions descriptively — the name should make the call site easier to read than the original inline code.
98+
99+
## Step 5: Apply the Refactor
100+
101+
Make the changes. Focus on one function per run to keep PRs reviewable.
102+
103+
## Step 6: Verify
104+
105+
Run the full test suite and Clippy to confirm:
106+
107+
```bash
108+
# Tests must pass
109+
cargo test
110+
111+
# Clippy must be clean
112+
cargo clippy --all-targets --all-features
113+
114+
# Re-run complexity check on the refactored function
115+
cargo clippy --all-targets --all-features --message-format=json -- \
116+
-W clippy::cognitive_complexity 2>/dev/null \
117+
| jq -c 'select(.reason == "compiler-message")
118+
| select(.message.code.code == "clippy::cognitive_complexity")
119+
| select(.message.spans[0].file_name == "<FILE>")
120+
| .message.message'
121+
```
122+
123+
If the complexity is not reduced, reconsider the approach. If tests fail, fix or revert.
124+
125+
## Step 7: Update Memory
126+
127+
Save state so the next run picks up where this one left off:
128+
129+
```json
130+
{
131+
"last_processed": "<file>:<function_name>",
132+
"date": "<today>",
133+
"action": "refactored|skipped|no-candidates",
134+
"original_complexity": <N>,
135+
"new_complexity": <N>,
136+
"history": ["<previous entries>"]
137+
}
138+
```
139+
140+
Write this to `/tmp/gh-aw/cache-memory/complexity-state.json`.
141+
142+
## Step 8: Submit
143+
144+
If changes were made and all checks pass, create a pull request:
145+
- **Title**: `refactor: reduce complexity of <function_name> in <file>`
146+
- **Description**: Explain what was complex, what you changed, and the before/after complexity scores.
147+
148+
### When No Action Is Needed
149+
150+
If no functions exceed the complexity threshold, or all candidates were already processed, use `noop` with a message like: "No functions exceed the cognitive complexity threshold. Codebase is in good shape."

0 commit comments

Comments
 (0)