-
Notifications
You must be signed in to change notification settings - Fork 1
feat: complexity-gated AI review triggers after CR rounds (#362) #441
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 all commits
3af327b
2991d70
7edcadd
17cf83d
bb1751e
abd13f7
c3ca07f
dac0377
4d891e1
518194a
17e19c8
6d34106
0473032
8a95393
9ca97b2
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 |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| # PM Config | ||
|
|
||
| ## Role | ||
|
|
||
| <!-- Optional: who uses this config (human-editable). --> | ||
|
|
||
| ## OKRs | ||
|
|
||
| No OKRs set — add objectives under this header when ready. | ||
|
|
||
| ## Complexity triggers | ||
|
|
||
| <!-- Issue #362 — tune per repo. Defaults match claude-code-config calibration (25 merged PRs, threshold 100 → 72% would exceed). --> | ||
|
|
||
| ```ini | ||
| THRESHOLD_SCORE=100 | ||
| FIRST_CR_ROUND=3 | ||
| CADENCE_ROUNDS=2 | ||
| FILE_WEIGHT=5 | ||
| ENABLE_PR_REVIEW_HELP=0 | ||
| ``` | ||
|
|
||
| - **THRESHOLD_SCORE** — minimum `complexity-score.sh` value before auto-trigger; must be a **non-negative integer**. Repo file sets the default; **`COMPLEXITY_THRESHOLD_SCORE` env overrides** when set. | ||
| - **FIRST_CR_ROUND** — first fire at this CodeRabbit round count (must be **≥ 3**; scripts error out otherwise — needs ≥ 2 completed CR rounds before first fire). Uses `cycle-count.sh <PR> --cr-only`. **`COMPLEXITY_FIRST_CR_ROUND` env overrides** when set. | ||
| - **CADENCE_ROUNDS** — after the first fire, fire again every N additional CR rounds (e.g. 2 → rounds 3, 5, 7…); must be **≥ 1**. **`COMPLEXITY_CADENCE_ROUNDS` env overrides** when set. | ||
| - **FILE_WEIGHT** — multiplier on `changedFiles` inside the score; must be a **positive integer** (0 and non-positive values are rejected). **`COMPLEXITY_FILE_WEIGHT` env overrides** when set. | ||
| - **ENABLE_PR_REVIEW_HELP** — `1` / `true` / `yes` / `on` posts a fourth comment `/pr-review-help #<PR_NUMBER>` after the three single-mention triggers. | ||
|
|
||
| ## Team | ||
|
|
||
| <!-- Optional: contributor display names. --> | ||
|
|
||
| ## Notes | ||
|
|
||
| <!-- Free-form. --> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| #!/usr/bin/env bash | ||
| # complexity-score.sh — PR complexity score for issue #362 auto-triggers. | ||
| # | ||
| # Formula (documented in `.claude/rules/cr-github-review.md`): | ||
| # score = additions + deletions + (file_weight × changedFiles) | ||
| # | ||
| # Default file_weight=5 matches calibration on this repo (merged PRs sample); | ||
| # override via `.claude/pm-config.md` section **Complexity triggers** (`FILE_WEIGHT=N`) | ||
| # or env `COMPLEXITY_FILE_WEIGHT`. | ||
| # | ||
| # Usage: | ||
| # complexity-score.sh <pr_number> [--json] | ||
| # complexity-score.sh --help | -h | ||
| # | ||
| # Exit: 0 OK, 2 usage, 3 PR not found, 4 gh/jq error | ||
|
|
||
| set -euo pipefail | ||
| printf '%s\t%s\t%s\n' "$(date -u +%FT%TZ)" "$(basename "$0")" "${*//$'\n'/ }" >> "$HOME/.claude/script-usage.log" 2>/dev/null || true | ||
|
|
||
| help() { | ||
| sed -n '2,15p' "$0" | sed 's/^# \{0,1\}//' | ||
| } | ||
|
|
||
| PR_NUM="" | ||
| JSON_OUT=0 | ||
| while [[ $# -gt 0 ]]; do | ||
| case "$1" in | ||
| -h|--help) help; exit 0 ;; | ||
| --json) JSON_OUT=1; shift ;; | ||
| -*) | ||
| echo "complexity-score.sh: unknown flag: $1" >&2 | ||
| exit 2 | ||
| ;; | ||
| *) | ||
| if [[ -n "$PR_NUM" ]]; then | ||
| echo "complexity-score.sh: unexpected argument: $1" >&2 | ||
| exit 2 | ||
| fi | ||
| PR_NUM="$1" | ||
| shift | ||
| ;; | ||
| esac | ||
| done | ||
|
|
||
| if [[ -z "$PR_NUM" ]] || ! [[ "$PR_NUM" =~ ^[1-9][0-9]*$ ]]; then | ||
| echo "usage: complexity-score.sh <pr_number> [--json]" >&2 | ||
| exit 2 | ||
| fi | ||
|
|
||
| if ! command -v gh >/dev/null 2>&1 || ! command -v jq >/dev/null 2>&1; then | ||
| echo "complexity-score.sh: requires gh and jq" >&2 | ||
| exit 4 | ||
| fi | ||
|
|
||
| FILE_WEIGHT=5 | ||
|
|
||
| # Optional: read FILE_WEIGHT from repo .claude/pm-config.md (Complexity triggers section) | ||
| REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)" | ||
| PM_CFG="" | ||
| [[ -n "$REPO_ROOT" && -f "$REPO_ROOT/.claude/pm-config.md" ]] && PM_CFG="$REPO_ROOT/.claude/pm-config.md" | ||
| if [[ -n "$PM_CFG" ]]; then | ||
| section=$(awk '/^## Complexity triggers/,/^## / { if (/^## Complexity triggers/) next; if (/^## /) exit; print }' "$PM_CFG" 2>/dev/null || true) | ||
|
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. Suggestion: The section extractor uses an awk range that starts at Severity Level: Major
|
||
| if [[ -n "$section" ]]; then | ||
| line=$(echo "$section" | grep -E '^[[:space:]]*FILE_WEIGHT[[:space:]]*=' | head -1 || true) | ||
| if [[ -n "$line" ]]; then | ||
| val="${line#*=}" | ||
| val="${val// /}" | ||
| val="${val//$'\t'/}" | ||
| if [[ "$val" =~ ^[1-9][0-9]*$ ]]; then | ||
| FILE_WEIGHT="$val" | ||
| else | ||
| echo "complexity-score.sh: pm-config FILE_WEIGHT='$val' is not a positive integer" >&2 | ||
| exit 4 | ||
| fi | ||
| fi | ||
| fi | ||
| fi | ||
|
coderabbitai[bot] marked this conversation as resolved.
auerbachb marked this conversation as resolved.
|
||
|
|
||
| # Env wins when set (explicit override for CI / one-off tuning). | ||
| if [[ "${COMPLEXITY_FILE_WEIGHT+set}" == "set" ]]; then | ||
| if [[ "${COMPLEXITY_FILE_WEIGHT}" =~ ^[1-9][0-9]*$ ]]; then | ||
| FILE_WEIGHT="$COMPLEXITY_FILE_WEIGHT" | ||
| else | ||
| echo "complexity-score.sh: COMPLEXITY_FILE_WEIGHT='$COMPLEXITY_FILE_WEIGHT' is not a positive integer" >&2 | ||
| exit 4 | ||
| fi | ||
| fi | ||
|
|
||
| STDERR_TMP="$(mktemp)" | ||
| if ! META="$(gh pr view "$PR_NUM" --json additions,deletions,changedFiles 2>"$STDERR_TMP")"; then | ||
| if grep -qiE 'not.?found|could not resolve|no pull requests? found' "$STDERR_TMP"; then | ||
| rm -f "$STDERR_TMP" | ||
| echo "complexity-score.sh: PR #$PR_NUM not found" >&2 | ||
| exit 3 | ||
| fi | ||
| cat "$STDERR_TMP" >&2 | ||
| rm -f "$STDERR_TMP" | ||
| exit 4 | ||
| fi | ||
| rm -f "$STDERR_TMP" | ||
|
|
||
| ADD=$(printf '%s' "$META" | jq -r '.additions') | ||
| DEL=$(printf '%s' "$META" | jq -r '.deletions') | ||
| FILES=$(printf '%s' "$META" | jq -r '.changedFiles') | ||
| SCORE=$(( ADD + DEL + FILE_WEIGHT * FILES )) | ||
|
|
||
| if (( JSON_OUT )); then | ||
| jq -n \ | ||
| --argjson score "$SCORE" \ | ||
| --argjson additions "$ADD" \ | ||
| --argjson deletions "$DEL" \ | ||
| --argjson changedFiles "$FILES" \ | ||
| --argjson fileWeight "$FILE_WEIGHT" \ | ||
| '{ | ||
| score: $score, | ||
| additions: $additions, | ||
| deletions: $deletions, | ||
| changedFiles: $changedFiles, | ||
| file_weight: $fileWeight, | ||
| formula: "additions + deletions + file_weight * changedFiles" | ||
| }' | ||
| else | ||
| printf '%s\n' "$SCORE" | ||
| fi | ||
| exit 0 | ||
Uh oh!
There was an error while loading. Please reload this page.