Skip to content

t1317: Migrate routine-scheduler should_run_routine() to AI scheduling#2220

Merged
marcusquinn merged 1 commit intomainfrom
feature/t1317
Feb 24, 2026
Merged

t1317: Migrate routine-scheduler should_run_routine() to AI scheduling#2220
marcusquinn merged 1 commit intomainfrom
feature/t1317

Conversation

@marcusquinn
Copy link
Owner

@marcusquinn marcusquinn commented Feb 24, 2026

Summary

  • Migrate should_run_routine() from 120-line case-statement decision tree to AI-driven scheduling via Anthropic API (haiku model)
  • Single batch API call evaluates all 5 routines per pulse cycle (cost-efficient: 1 call instead of 5)
  • Deterministic heuristic fallback preserved when AI unavailable (no auth, API failure, timeout)
  • Hard constraints (min_interval floor, explicit deferrals) always enforced — AI cannot override safety floors
  • Auth resolution supports both ANTHROPIC_API_KEY env var and OAuth from auth.json

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 fallback
  • should_run_routine() — rewritten to check AI cache first, fall back to heuristic
  • run_phase14_routine_scheduler() — attempts AI batch evaluation, logs method used

Testing

  • ShellCheck passes clean (zero violations)
  • API contract unchanged: callers still get "run"/"skip"/"defer" on stdout, return 0/1
  • ROUTINE_DECISION_* env var exports unchanged — pulse.sh consumers unaffected

Ref #2178

Summary by CodeRabbit

Release Notes

  • New Features

    • Routine scheduling now leverages AI-powered decision-making for optimized routine execution.
  • Improvements

    • Added automatic fallback to deterministic logic when AI services are unavailable, ensuring scheduling continuity.
    • Enhanced logging to indicate which scheduling method was applied to each routine decision.

…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)
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 24, 2026

Walkthrough

Integrates 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

Cohort / File(s) Summary
AI-Driven Scheduling Integration
.agents/scripts/supervisor/routine-scheduler.sh
Introduces AI batch evaluation flow with Anthropic API integration, authentication resolution, and decision caching. Implements heuristic fallback logic with metrics tracking (elapsed, interval, findings, deferrals). Adds helper functions for auth and AI scheduling, verbose logging with [ai] and [heuristic] prefixes, and two-path routing for routine decisions while preserving hard constraints (skip_until, min_interval).

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🤖 When Anthropic whispers scheduling dreams,
But networks falter mid-API streams,
The heuristic guardian stands steady and true,
With decision cache and metrics to pursue—
Phase 14 routines dance in dual light!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: migrating the routine scheduler from heuristic-based decisions to AI-driven scheduling using the Anthropic model.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/t1317

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@marcusquinn marcusquinn marked this pull request as ready for review February 24, 2026 17:03
@github-actions
Copy link

🔍 Code Quality Report

�[0;35m[MONITOR]�[0m Code Review Monitoring Report

�[0;34m[INFO]�[0m Latest Quality Status:
SonarCloud: 0 bugs, 0 vulnerabilities, 41 code smells

�[0;34m[INFO]�[0m Recent monitoring activity:
Tue Feb 24 17:03:11 UTC 2026: Code review monitoring started
Tue Feb 24 17:03:12 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 41

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 41
  • VULNERABILITIES: 0

Generated on: Tue Feb 24 17:03:14 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

@gemini-code-assist
Copy link

Summary of Changes

Hello @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

  • AI-Driven Routine Scheduling: This PR migrates the should_run_routine() function to use AI-driven scheduling via the Anthropic API (haiku model), replacing a 120-line case statement.
  • Batch API Call: The implementation uses a single batch API call to evaluate all 5 routines per pulse cycle, optimizing cost and efficiency.
  • Deterministic Heuristic Fallback: A deterministic heuristic fallback is preserved for situations where the AI is unavailable (e.g., no auth, API failure, timeout), ensuring continued operation.
  • Hard Constraint Enforcement: Hard constraints such as minimum interval floors and explicit deferrals are always enforced, preventing the AI from overriding safety measures.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@sonarqubecloud
Copy link

@gemini-code-assist
Copy link

Warning

Gemini is experiencing higher than usual traffic and was unable to create the review. Please try again in a few hours by commenting /gemini review.

@marcusquinn marcusquinn merged commit 418a7af into main Feb 24, 2026
10 of 11 checks passed
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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), sed won't match at all, and $decisions_json falls back to the full $ai_text on line 251, which jq may 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" if should_run_routine produces no stdout. This means any unexpected failure in the decision pipeline causes all routines to execute. For lightweight routines like memory_audit this 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 the readonly constants at the top of the file. If thresholds change, both locations need updating.

Consider generating the system prompt dynamically from the readonly threshold 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.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b667429 and 8f6ff26.

📒 Files selected for processing (1)
  • .agents/scripts/supervisor/routine-scheduler.sh

Comment on lines 559 to 562
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
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.

@marcusquinn
Copy link
Owner Author

Resolves #2213.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant