143215a7a2ed0bf37f565d6463ca11964b69dc80
jnavarr5
  Wed Jul 29 11:02:35 2026 -0700
Floor the scaled daily code review timeout at the previous flat 600s so 1-2 commit authors keep the same runway, No RM.

diff --git src/utils/codeReviewAi.py src/utils/codeReviewAi.py
index 81f8a4fdbc4..a1fe83010aa 100755
--- src/utils/codeReviewAi.py
+++ src/utils/codeReviewAi.py
@@ -34,32 +34,34 @@
 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.
+# The floor keeps small batches at the old flat 600s rather than shrinking them.
 DAILY_TIMEOUT_BASE = 300
 DAILY_TIMEOUT_PER_COMMIT = 120
+DAILY_TIMEOUT_MIN = 600
 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()
@@ -1370,31 +1372,32 @@
 
     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}")
 
     timeout = min(DAILY_TIMEOUT_MAX,
-                  DAILY_TIMEOUT_BASE + DAILY_TIMEOUT_PER_COMMIT * len(commits))
+                  max(DAILY_TIMEOUT_MIN,
+                      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"