6be32a42dac8ea214aedb3ae97f7b9d01d183b5a jnavarr5 Tue Jul 28 17:25:30 2026 -0700 Scale the daily code review timeout with the commit count so large batches stop timing out, No RM. diff --git src/utils/codeReviewAi.py src/utils/codeReviewAi.py index 9a0dadd1d72..81f8a4fdbc4 100755 --- src/utils/codeReviewAi.py +++ src/utils/codeReviewAi.py @@ -31,30 +31,36 @@ # Configuration REDMINE_URL = "https://redmine.gi.ucsc.edu" GIT_REPORTS_PATH = "/hive/groups/qa/git-reports-history" GIT_REPO_PATH = "/data/git/kent.git" OUTPUT_DIR = f"/hive/users/{getpass.getuser()}/codeReview" MLQ_CONF_PATH = os.path.expanduser("~/.hg.conf") DEFAULT_CC = "browser-code-reviews-group@ucsc.edu" DEFAULT_ALERT_EMAIL = "browserqa-group@ucsc.edu" GMAIL_TOKEN_PATH = os.path.expanduser("~/.gmail_token.json") GMAIL_CREDS_PATH = os.path.expanduser("~/.gmail_credentials.json") GMAIL_SCOPES = [ 'https://www.googleapis.com/auth/gmail.send', ] CLAUDE_CLI = os.path.expanduser('~/.local/bin/claude') +# Daily reviews get a timeout scaled to the batch size. A flat 600s limit timed +# out repeatedly on 8-11 commit days, and since the retry reused the same limit +# those authors' commits went unreviewed with no later window to catch them. +DAILY_TIMEOUT_BASE = 300 +DAILY_TIMEOUT_PER_COMMIT = 120 +DAILY_TIMEOUT_MAX = 2400 def load_config(): """Load API keys from ~/.hg.conf""" config = {} if not os.path.exists(MLQ_CONF_PATH): print(f"ERROR: Config file not found: {MLQ_CONF_PATH}") sys.exit(1) with open(MLQ_CONF_PATH, 'r') as f: for line in f: line = line.strip() if '=' in line and not line.startswith('#'): key, value = line.split('=', 1) config[key.strip()] = value.strip() @@ -1362,33 +1368,36 @@ print(f"Commits: {len(commits)}") print(f"{'='*60}") prompt = build_daily_review_prompt(author_name, commits, window_label) # Save prompt to log_dir for debugging (cleaned up on success) safe_name = re.sub(r'[^a-zA-Z0-9]', '_', author_name) suffix = file_suffix or datetime.now().strftime('%Y%m%d') temp_files = [] prompt_file = os.path.join(log_dir, f".tmp_daily_prompt_{safe_name}_{suffix}.txt") with open(prompt_file, 'w') as f: f.write(prompt) temp_files.append(prompt_file) print(f" Prompt saved to: {prompt_file}") - print(f" Calling Claude CLI (this may take a few minutes)...") - raw_response = call_claude_cli(prompt, timeout=600, validator=validate_daily_review_output) + timeout = min(DAILY_TIMEOUT_MAX, + DAILY_TIMEOUT_BASE + DAILY_TIMEOUT_PER_COMMIT * len(commits)) + print(f" Calling Claude CLI (timeout {timeout}s for {len(commits)} commit(s))...") + raw_response = call_claude_cli(prompt, timeout=timeout, + validator=validate_daily_review_output) error = detect_cli_failure(raw_response, validate_daily_review_output) # Save whatever we got back for debugging, even on failure. if raw_response: response_file = os.path.join(log_dir, f".tmp_daily_response_{safe_name}_{suffix}.txt") with open(response_file, 'w') as f: f.write(raw_response) temp_files.append(response_file) if error: print(f" WARNING: review failed - {error}") response = f"DAILY CODE REVIEW - {author_name}\n\nError: {error}\n" else: response = raw_response # Strip any preamble before "DAILY CODE REVIEW"