t1317: Migrate routine-scheduler should_run_routine() to AI scheduling#2220
t1317: Migrate routine-scheduler should_run_routine() to AI scheduling#2220marcusquinn merged 1 commit intomainfrom
Conversation
…duling (t1317) Replace the 120-line case-statement decision tree in should_run_routine() with AI-driven scheduling via Anthropic API (haiku model). Key changes: - Add _resolve_anthropic_auth(): resolves auth from ANTHROPIC_API_KEY env var or OAuth token from ~/.local/share/opencode/auth.json (matches TypeScript pattern) - Add _ai_schedule_all_routines(): single batch API call evaluates all 5 routines at once, populating a session-scoped cache. Sends routine state + project signals as structured JSON, receives run/skip/defer decisions per routine. - Rename original case-statement logic to _heuristic_should_run_routine() as deterministic fallback when AI is unavailable (no auth, API failure, timeout) - Rewrite should_run_routine() to check AI cache first, fall back to heuristic - Hard constraints (min_interval floor, explicit deferrals) are ALWAYS enforced regardless of AI decision — the AI cannot override safety floors - run_phase14_routine_scheduler() now attempts AI batch evaluation first, logs which method (ai/heuristic) was used for auditability - All log messages tagged with [ai] or [heuristic] for observability Design decisions (chose batch over per-routine calls — 1 API call per pulse instead of 5, matching cost-efficiency principle; chose haiku model for lowest cost; 10s timeout ensures pulse is never blocked by API latency)
WalkthroughIntegrates Anthropic AI-based batch evaluation for Phase 14 routine scheduling decisions with deterministic heuristic fallback when AI is unavailable. Adds authentication resolution, per-routine metrics tracking, new helper functions, and verbose logging to distinguish between AI and heuristic decision paths while preserving hard constraints. Changes
Sequence Diagram(s)sequenceDiagram
participant PS as Phase 14 Scheduler
participant ARZ as Auth Resolver
participant API as Anthropic API
participant CACHE as Decision Cache
participant HE as Heuristic Engine
participant RT as Routine Executor
PS->>ARZ: Resolve Anthropic credentials
alt Auth Available
ARZ->>API: Send batch request (routine metrics)
API-->>CACHE: Populate with AI decisions
note over CACHE: run|skip|defer per routine
else Auth Unavailable
note over ARZ: Fallback to heuristic
end
loop For Each Routine
RT->>CACHE: Consult decision cache
alt Valid AI Decision
CACHE-->>RT: Use AI decision [ai]
else Cache Miss/Invalid
RT->>HE: Evaluate heuristic rules
HE-->>RT: Return decision [heuristic]
end
RT->>RT: Apply hard constraints
RT->>RT: Execute routine if run decision
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🔍 Code Quality Report�[0;35m[MONITOR]�[0m Code Review Monitoring Report �[0;34m[INFO]�[0m Latest Quality Status: �[0;34m[INFO]�[0m Recent monitoring activity: 📈 Current Quality Metrics
Generated on: Tue Feb 24 17:03:14 UTC 2026 Generated by AI DevOps Framework Code Review Monitoring |
Summary of ChangesHello @marcusquinn, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces AI-driven scheduling for supervisor routines, enhancing the system's adaptability and efficiency. It leverages the Anthropic API to make intelligent decisions about when to run, skip, or defer routines based on project state signals. A heuristic fallback ensures reliability when the AI is unavailable, and hard constraints guarantee safety and stability. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
|
|
Warning Gemini is experiencing higher than usual traffic and was unable to create the review. Please try again in a few hours by commenting |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
.agents/scripts/supervisor/routine-scheduler.sh (3)
247-252: JSON extraction regex is fragile — can break on multi-line or nested responses.The
sed -n 's/.*\({.*}\).*/\1/p'operates per-line and uses greedy matching. If the AI returns the JSON across multiple lines (common with pretty-printed output),sedwon't match at all, and$decisions_jsonfalls back to the full$ai_texton line 251, whichjqmay then fail to parse — triggering heuristic fallback.This is "safe" because the fallback catches it, but it means the AI path silently fails more often than necessary.
Suggested improvement: pipe through jq directly
- # Extract JSON from response (AI may wrap in markdown code blocks) - local decisions_json - decisions_json=$(echo "$ai_text" | sed -n 's/.*\({.*}\).*/\1/p' | head -1) - if [[ -z "$decisions_json" ]]; then - decisions_json="$ai_text" - fi + # Extract JSON from response (AI may wrap in markdown code blocks) + local decisions_json + # Strip markdown fences, then parse first valid JSON object + decisions_json=$(echo "$ai_text" | sed 's/^```[a-z]*//;s/^```//' | jq -s '.[0]' 2>/dev/null) + if [[ -z "$decisions_json" ]] || [[ "$decisions_json" == "null" ]]; then + decisions_json="$ai_text" + fi🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/scripts/supervisor/routine-scheduler.sh around lines 247 - 252, The JSON extraction using sed is fragile for multi-line or pretty-printed JSON; replace the sed-based extraction for decisions_json with a pipeline that strips markdown fences/language tags from ai_text and passes the cleaned text into jq (e.g., use jq -s '.[0]' to coalesce multiple documents) to safely parse multi-line/nested JSON into decisions_json, then check if decisions_json is empty or "null" and only then fall back to using the raw ai_text; update the variables referenced (decisions_json, ai_text) and remove the sed command usage to ensure jq does the heavy lifting.
758-781: Default-to-"run"on empty output is an intentional fail-open — verify this is the desired posture.Each
${mem_decision:-run}(and similar) defaults to"run"ifshould_run_routineproduces no stdout. This means any unexpected failure in the decision pipeline causes all routines to execute. For lightweight routines likememory_auditthis is fine, but for heavier routines (coderabbit,skill_update), a fail-open default means unnecessary work under error conditions.If the project prefers fail-safe (skip on error) over fail-open (run on error) for heavier routines, consider defaulting those to
"skip"instead. Otherwise, this is a reasonable "when in doubt, maintain the schedule" approach.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/scripts/supervisor/routine-scheduler.sh around lines 758 - 781, The current logic defaults any empty/failed should_run_routine stdout to "run", which fails open; change the failover to "skip" for the heavier routines by replacing the default expansions for the relevant exports (use ROUTINE_DECISION_CODERABBIT, ROUTINE_DECISION_TASK_CREATION and ROUTINE_DECISION_SKILL_UPDATE) so that after calling should_run_routine(...) you export each as "${var_name:-skip}" instead of "run" (leave lightweight ones like ROUTINE_DECISION_MEMORY_AUDIT or ROUTINE_DECISION_MODELS_MD unchanged if you want them to remain fail-open); keep the should_run_routine invocation and error suppression as-is.
185-211: System prompt encodes the same rules as the heuristic — good parity.This makes the AI path produce decisions consistent with the fallback logic. One observation: the prompt's threshold values (e.g.,
consecutive_zero_findings >= 3,elapsed < 604800) are duplicated from thereadonlyconstants at the top of the file. If thresholds change, both locations need updating.Consider generating the system prompt dynamically from the
readonlythreshold constants to keep a single source of truth:# Example: inject thresholds into the prompt template system_prompt="...skip coderabbit if consecutive_zero_findings >= ${ROUTINE_SKIP_THRESHOLD_CODERABBIT}..."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/scripts/supervisor/routine-scheduler.sh around lines 185 - 211, The system_prompt currently hardcodes thresholds duplicated from the top-of-file readonly constants; update the code to build system_prompt dynamically by interpolating the existing threshold constants (e.g., ROUTINE_SKIP_THRESHOLD_CODERABBIT, ROUTINE_SKIP_THRESHOLD_TASK_CREATION, ROUTINE_ELAPSED_WEEKLY, ROUTINE_ELAPSED_DAILY, ROUTINE_DEFER_CRITICAL_ISSUES, ROUTINE_DEFER_RECENT_FAILURES) into the prompt string (the symbol to change is system_prompt) so the prompt always reflects the single source-of-truth constants; keep the rest of the request_body construction (ROUTINE_AI_MODEL, ROUTINE_AI_MAX_TOKENS, messages) intact and ensure proper shell variable expansion/quoting when injecting numeric values into the prompt.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.agents/scripts/supervisor/routine-scheduler.sh:
- Around line 559-562: The log message and the decision output are inconsistent:
update the log_info call in the routine-scheduler block that currently says
'Phase 14: [heuristic] task_creation deferred' to instead say 'Phase 14:
[heuristic] task_creation skip' (or otherwise match the echoed decision) so it
matches the echo "skip" sent as the ROUTINE_DECISION_TASK_CREATION result;
specifically modify the log_info invocation near the echo "skip" in the
routine-scheduler.sh routine that handles phase 14 task_creation so the logged
text and the exported decision are identical.
---
Nitpick comments:
In @.agents/scripts/supervisor/routine-scheduler.sh:
- Around line 247-252: The JSON extraction using sed is fragile for multi-line
or pretty-printed JSON; replace the sed-based extraction for decisions_json with
a pipeline that strips markdown fences/language tags from ai_text and passes the
cleaned text into jq (e.g., use jq -s '.[0]' to coalesce multiple documents) to
safely parse multi-line/nested JSON into decisions_json, then check if
decisions_json is empty or "null" and only then fall back to using the raw
ai_text; update the variables referenced (decisions_json, ai_text) and remove
the sed command usage to ensure jq does the heavy lifting.
- Around line 758-781: The current logic defaults any empty/failed
should_run_routine stdout to "run", which fails open; change the failover to
"skip" for the heavier routines by replacing the default expansions for the
relevant exports (use ROUTINE_DECISION_CODERABBIT,
ROUTINE_DECISION_TASK_CREATION and ROUTINE_DECISION_SKILL_UPDATE) so that after
calling should_run_routine(...) you export each as "${var_name:-skip}" instead
of "run" (leave lightweight ones like ROUTINE_DECISION_MEMORY_AUDIT or
ROUTINE_DECISION_MODELS_MD unchanged if you want them to remain fail-open); keep
the should_run_routine invocation and error suppression as-is.
- Around line 185-211: The system_prompt currently hardcodes thresholds
duplicated from the top-of-file readonly constants; update the code to build
system_prompt dynamically by interpolating the existing threshold constants
(e.g., ROUTINE_SKIP_THRESHOLD_CODERABBIT, ROUTINE_SKIP_THRESHOLD_TASK_CREATION,
ROUTINE_ELAPSED_WEEKLY, ROUTINE_ELAPSED_DAILY, ROUTINE_DEFER_CRITICAL_ISSUES,
ROUTINE_DEFER_RECENT_FAILURES) into the prompt string (the symbol to change is
system_prompt) so the prompt always reflects the single source-of-truth
constants; keep the rest of the request_body construction (ROUTINE_AI_MODEL,
ROUTINE_AI_MAX_TOKENS, messages) intact and ensure proper shell variable
expansion/quoting when injecting numeric values into the prompt.
| if [[ "$elapsed" -lt "$daily_interval" ]]; then | ||
| log_info " Phase 14: task_creation deferred — ${consecutive_zero} consecutive empty runs" | ||
| log_info " Phase 14: [heuristic] task_creation deferred — ${consecutive_zero} consecutive empty runs" | ||
| echo "skip" | ||
| return 1 |
There was a problem hiding this comment.
Log says "deferred" but the decision output is "skip" — semantic mismatch.
Line 560 logs task_creation deferred while line 561 echoes "skip". This inconsistency will cause confusion when debugging scheduling behavior from logs, since the log and the exported ROUTINE_DECISION_TASK_CREATION value will disagree.
Align the log message with the actual decision
- log_info " Phase 14: [heuristic] task_creation deferred — ${consecutive_zero} consecutive empty runs"
+ log_info " Phase 14: [heuristic] task_creation skipped — ${consecutive_zero} consecutive empty runs"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if [[ "$elapsed" -lt "$daily_interval" ]]; then | |
| log_info " Phase 14: task_creation deferred — ${consecutive_zero} consecutive empty runs" | |
| log_info " Phase 14: [heuristic] task_creation deferred — ${consecutive_zero} consecutive empty runs" | |
| echo "skip" | |
| return 1 | |
| if [[ "$elapsed" -lt "$daily_interval" ]]; then | |
| log_info " Phase 14: [heuristic] task_creation skipped — ${consecutive_zero} consecutive empty runs" | |
| echo "skip" | |
| return 1 |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.agents/scripts/supervisor/routine-scheduler.sh around lines 559 - 562, The
log message and the decision output are inconsistent: update the log_info call
in the routine-scheduler block that currently says 'Phase 14: [heuristic]
task_creation deferred' to instead say 'Phase 14: [heuristic] task_creation
skip' (or otherwise match the echoed decision) so it matches the echo "skip"
sent as the ROUTINE_DECISION_TASK_CREATION result; specifically modify the
log_info invocation near the echo "skip" in the routine-scheduler.sh routine
that handles phase 14 task_creation so the logged text and the exported decision
are identical.
|
Resolves #2213. |



Summary
should_run_routine()from 120-line case-statement decision tree to AI-driven scheduling via Anthropic API (haiku model)Changes
_resolve_anthropic_auth()— bash auth resolution matching TypeScript ai-research pattern_ai_schedule_all_routines()— batch API call with structured JSON context_heuristic_should_run_routine()— renamed original case-statement as fallbackshould_run_routine()— rewritten to check AI cache first, fall back to heuristicrun_phase14_routine_scheduler()— attempts AI batch evaluation, logs method usedTesting
Ref #2178
Summary by CodeRabbit
Release Notes
New Features
Improvements