How AI Adaptation Works
TL;DR: The AI Planner analyzes endpoint execution patterns and writes time-bounded hints to adjust schedules. It has four action tools (propose_interval, propose_next_time, pause_until, clear_hints) and three query tools for response data. Structure your response bodies with metrics the AI should monitor.
This document explains how the AI Planner suggests schedule adjustments. See System Architecture for context on the dual-worker design.
The AI Planner's Jobβ
The AI Planner worker analyzes endpoint execution patterns and suggests scheduling adjustments by writing hints to the database. It doesn't execute jobs or manage locksβit observes and recommends.
The AI Planner runs automatically β no per-endpoint configuration is required. When AI is enabled on your account, the planner analyzes all endpoints with recent activity. There is no code to write, no rules engine to configure, and no parsing logic to implement. You provide descriptions and response bodies; the AI handles everything else.
End-to-End Processing Pipelineβ
Here is the complete flow from endpoint execution to AI-adjusted scheduling:
1. SCHEDULER EXECUTES ENDPOINT
ββ Makes HTTP request to your URL
ββ Records: status (success/failed), duration, response body
ββ Updates failure count (reset on 2xx, increment on non-2xx/timeout)
2. AI PLANNER DISCOVERS ENDPOINT (every 5 minutes)
ββ Queries for endpoints with recent activity
ββ Checks: is this endpoint due for analysis?
β ββ First analysis (never analyzed before)
β ββ Scheduled time passed (AI requested re-analysis)
β ββ State change (new failures since last analysis)
ββ Skips endpoints not due for analysis
3. AI BUILDS CONTEXT (per endpoint)
ββ Endpoint config: name, description, baseline, min/max constraints
ββ Health metrics: success rates over 1h, 4h, 24h windows
ββ Active hints: current interval/one-shot hints and expiry
ββ Job context: job description, sibling endpoint names
4. AI CALLS TOOLS (max 15 calls per session)
ββ Query tools: get_latest_response, get_response_history,
β get_sibling_latest_responses
ββ Action tools: propose_interval, propose_next_time,
β pause_until, clear_hints
ββ Terminal tool: submit_analysis (ends session, sets next analysis time)
5. HINTS WRITTEN TO DATABASE
ββ ai_hint_interval_ms: proposed frequency override
ββ ai_hint_next_run_at: one-shot execution time
ββ ai_hint_expires_at: TTL expiration
ββ ai_hint_reason: explanation for audit trail
6. SCHEDULER PICKS UP HINTS (next execution cycle)
ββ Governor calculates next run using priority order:
β 1. Pause (highest priority β stops all execution)
β 2. AI hints (interval overrides baseline, one-shot competes)
β 3. Baseline schedule + exponential backoff
β 4. Clamp to min/max constraints (always applied)
ββ Updates nextRunAt in database
7. CYCLE REPEATS
ββ Scheduler executes β AI analyzes β hints applied β ...
Key points:
- The Scheduler and AI Planner are independent workers communicating only through the database
- AI hints are temporary (TTL-based) β they expire and revert to baseline automatically
- The Scheduler never stops even if the AI is unavailable β it continues on baseline schedules
- AI analysis sessions are recorded for debugging (tools called, reasoning, token usage)
Discovery: Finding Endpoints to Analyzeβ
The AI Planner uses smart scheduling where it controls when to analyze again:
- Query the database for recently active endpoints
- Check if the endpoint is due for analysis:
- First analysis: New endpoints never analyzed
- Scheduled time: AI-requested re-analysis time has passed
- State change: New failures since last analysis (triggers immediate re-analysis)
- Skip endpoints where none of these conditions are met
The AI communicates its preferred re-analysis time via next_analysis_in_ms in submit_analysis.
What the AI Seesβ
For each endpoint, the AI receives:
Configurationβ
- Baseline schedule (cron or interval)
- Constraints (min/max intervals)
- Pause state and active hints
- Failure count
Multi-Window Health Metricsβ
| Window | Metrics |
|---|---|
| Last 1 hour | Success rate, run count |
| Last 4 hours | Success rate, run count |
| Last 24 hours | Success rate, run count |
Plus average duration and failure streak.
Multiple windows matter for recovery detection. A 24-hour window can show 5% success even when recent performance is 100% due to old failures.
Response Body Dataβ
The AI can query response data three ways:
- Latest response: Current state snapshot
- Response history: Trends over time
- Sibling responses: Coordinate across endpoints in the same job
Job Contextβ
- Job description (high-level intent)
- Sibling endpoint names
Session Constraintsβ
Each analysis session is limited to prevent runaway costs:
- Maximum 15 tool calls per session
- 10 history records is usually sufficient
- Sessions hitting the limit are terminated
The Four Action Toolsβ
1. propose_intervalβ
Adjust how frequently the endpoint runs.
Parameters: intervalMs, ttlMinutes (default: 60), reason
When to use:
- Tighten monitoring during load spikes (5min β 30sec)
- Relax during stability (1min β 10min to save resources)
- Override exponential backoff after recovery
How it works: Writes aiHintIntervalMs and aiHintExpiresAt to database, then nudges nextRunAt to apply immediately.
2. propose_next_timeβ
Schedule a specific one-time execution.
Parameters: nextRunAtIso, ttlMinutes (default: 30), reason
When to use:
- Run immediately to investigate a failure
- Defer to off-peak hours
- Coordinate with external events
3. pause_untilβ
Stop execution temporarily or resume.
Parameters: untilIso (or null to resume), reason
When to use:
- Dependency is down
- Rate limit detected
- Maintenance window
4. clear_hintsβ
Reset endpoint to baseline schedule.
Parameters: reason
When to use:
- AI hints no longer relevant
- Manual intervention resolved the issue
- Revert to baseline without waiting for TTL
Query Toolsβ
get_latest_responseβ
Returns most recent response body, timestamp, status.
get_response_historyβ
Parameters: limit (1-10), offset
Returns array of response bodies with timestamps. 10 records is usually sufficient.
get_sibling_latest_responsesβ
Returns for each sibling endpoint:
- Response data (body, timestamp, status)
- Schedule info (baseline, next run, pause status)
- Active AI hints with expiry and reason
Hint Mechanicsβ
TTLs and Expirationβ
All hints expire automaticallyβthis is a safety mechanism:
- Short (5-15 min): Transient spikes, immediate investigations
- Medium (30-60 min): Operational shifts
- Long (2-4 hours): Sustained degradation, maintenance
When hints expire, the system falls back to baseline.
Override vs. Competeβ
AI interval hints OVERRIDE baseline: If active, Governor ignores baseline completely. Enables both tightening and relaxing.
AI one-shot hints COMPETE with baseline: Governor chooses earliest. Enables immediate runs.
For detailed constraint interaction, see How Scheduling Works.
Nudgingβ
When AI writes a hint, it nudges nextRunAt to apply immediately using setNextRunAtIfEarlier(timestamp):
T=0: Endpoint scheduled for T=300 (5 min baseline)
T=60: AI proposes 30-second interval
T=60: AI nudges nextRunAt to T=90
T=90: Scheduler executes endpoint
Without nudging, changes wait until T=300. With nudging, they apply at T=90.
Structuring Response Bodiesβ
The AI works with the data you provide. Structure response bodies to include:
Relevant Metricsβ
{
"queue_depth": 250,
"processing_rate_per_min": 80,
"error_rate_pct": 2.5,
"avg_latency_ms": 145,
"healthy": true
}
The AI looks for: queue, latency, error, rate, count, healthy, status.
Thresholdsβ
{
"queue_depth": 250,
"queue_max": 500,
"queue_warning_threshold": 300
}
Coordination Signalsβ
For multi-endpoint workflows:
{
"ready_for_processing": true,
"upstream_completed_at": "2025-11-02T14:45:00Z",
"needs_downstream_action": true
}
Stability Signalsβ
For volatile workloads:
{
"trend": "stable",
"within_normal_range": true,
"avg_5min": 487,
"recommendation": "maintain_interval"
}
Explicit Recommendationsβ
You can include scheduling hints:
{
"status": "degraded",
"recommendation": "increase_frequency",
"suggested_interval_ms": 30000
}
The AI interprets these as guidance, not commands.
Tipsβ
- Use consistent naming across responses
- Include timestamps for cooldown patterns
- Keep it simple (1000 char truncation)
- Include smoothed averages for volatile data
- Add explicit signals (
ready_for_*,needs_*,recommendation)
Decision Frameworkβ
The AI follows a conservative approach:
Default to stability: Most of the time, doing nothing is optimal.
Intervene when:
- Clear trend (5+ data points showing monotonic change)
- Threshold crossing
- State transition (dependency status changes)
- Coordination signal from sibling
Don't intervene when:
- Single anomaly (might be transient)
- Insufficient data (fewer than 10 total runs)
- Metrics within normal ranges
Complete AI Decision Examplesβ
These examples show the exact sequence of tool calls the AI makes in common scenarios.
Example: Tightening During Degradationβ
Endpoint: api-health-check (baseline 5 minutes, min 30 seconds)
Description: "Poll more frequently when error_rate_pct exceeds 5%. Return to baseline when below 2%."
- AI calls
get_latest_response()β Returns:{ "status": "degraded", "error_rate_pct": 8.5, "latency_ms": 1200 } - AI calls
get_response_history(limit=5)β Shows error_rate climbing: 2.1 β 3.5 β 5.2 β 7.1 β 8.5 - AI reads description: threshold is 5%, current is 8.5%, sustained upward trend across 5 readings
- AI calls
propose_interval(intervalMs=30000, ttlMinutes=60, reason="Error rate climbing to 8.5%, above 5% threshold - tightening to 30s") - Governor applies: next run in 30 seconds (within min constraint)
Example: Recovery and Return to Baselineβ
Endpoint: same api-health-check, currently running at 30-second hint
- AI calls
get_latest_response()β Returns:{ "status": "healthy", "error_rate_pct": 0.5, "latency_ms": 145 } - AI calls
get_response_history(limit=5)β Shows error_rate declining: 8.5 β 5.2 β 2.1 β 0.8 β 0.5 - AI reads description: "Return to baseline when below 2%" β current is 0.5%, sustained downward trend
- AI calls
clear_hints(reason="Error rate normalized to 0.5%, below 2% threshold - returning to baseline") - Governor falls back to baseline: next run in 5 minutes
Example: Data Volume Driving Frequencyβ
Endpoint: sync-status (baseline 10 minutes, min 30 seconds)
Description: "Poll every 30 seconds when records_pending > 1000. Return to baseline when below 100."
- AI calls
get_latest_response()β Returns:{ "records_pending": 15000, "sync_rate_per_minute": 500, "status": "syncing" } - AI reads description: "records_pending > 1000" threshold β current is 15000
- AI calls
propose_interval(intervalMs=30000, ttlMinutes=30, reason="15000 records pending, above 1000 threshold") - Next analysis: AI calls
get_latest_response()β{ "records_pending": 50, "status": "idle" } - AI reads description: "Return to baseline when below 100" β current is 50
- AI calls
clear_hints(reason="Records pending at 50, below 100 threshold - returning to baseline")
Example: Sibling-Triggered Recoveryβ
Endpoint: trigger-recovery (baseline 24 hours, min 5 minutes)
Description: "Only run when health-check sibling shows needs_recovery=true."
- AI calls
get_sibling_latest_responses()β Returns:{ "health-check": { "body": { "status": "error", "needs_recovery": true }, "statusCode": 500 } } - AI reads description: "run when health-check sibling shows needs_recovery=true"
- AI sees: health-check returned
needs_recovery: true - AI calls
propose_next_time(now, ttlMinutes=30, reason="Health-check shows needs_recovery=true, triggering immediate recovery") - Governor schedules: immediate execution
After recovery succeeds:
- AI calls
get_sibling_latest_responses()β Returns:{ "health-check": { "body": { "status": "ok", "needs_recovery": false } } } - AI reads description: no trigger condition met
- No action β trigger-recovery returns to 24-hour baseline
Example: Inverse Scaling (Reduce Load Under Pressure)β
Endpoint: system-load-monitor (baseline 1 minute, min 10 seconds, max 5 minutes)
Description: "INVERSE SCALING: when cpu_pct > 80, poll LESS frequently. When cpu_pct < 50, poll MORE frequently."
- AI calls
get_latest_response()β Returns:{ "cpu_pct": 92, "memory_pct": 78, "recommendation": "reduce_polling" } - AI reads description: "when cpu_pct > 80, poll LESS frequently" β current is 92
- AI calls
propose_interval(intervalMs=300000, ttlMinutes=30, reason="CPU at 92%, reducing polling to 5min to lower overhead")
When load drops:
- AI calls
get_latest_response()β Returns:{ "cpu_pct": 35, "recommendation": "normal" } - AI reads description: "when cpu_pct < 50, poll MORE frequently"
- AI calls
propose_interval(intervalMs=30000, ttlMinutes=30, reason="CPU at 35%, increasing polling - system has capacity")
Example: Oscillation Preventionβ
Endpoint: volatile-metrics (baseline 1 minute, min 30 seconds, max 2 minutes)
Description: "STABILITY PRIORITY. Only use avg_5min, never instant_value. Only act on sustained trends."
Transient spike β AI does NOT react:
- AI calls
get_latest_response()β Returns:{ "instant_value": 950, "avg_5min": 510, "trend": "stable", "within_normal_range": true } - AI reads description: "Only use avg_5min" β avg_5min is 510 (normal)
- AI reads description: "within_normal_range is true" β no action needed
- No action β maintains current interval despite extreme instant_value
- Reasoning: "Transient spike in instant_value but smoothed avg_5min is normal and trend is stable"
Genuine state change β AI does react:
- AI calls
get_latest_response()β Returns:{ "avg_5min": 780, "trend": "rising", "within_normal_range": false } - AI calls
get_response_history(limit=5)β avg_5min rising: 510 β 580 β 650 β 720 β 780 - AI reads description: "Only act on sustained trends" β 5 consecutive readings show rise
- AI calls
propose_interval(intervalMs=30000, ttlMinutes=30, reason="Sustained rise in avg_5min, outside normal range")
Stability and Oscillation Preventionβ
Cronicorn includes multiple mechanisms to prevent oscillation between extreme scheduling states.
Built-in Stability Mechanismsβ
1. Multi-Window Health Metrics
The AI sees success rates across three windows (1h, 4h, 24h). This prevents reacting to momentary spikesβthe AI compares windows to distinguish genuine state changes from transient noise.
2. Analysis Cooldown
The AI Planner doesn't analyze every response. Default 5-minute cooldown between analyses prevents rapid decision changes.
3. TTL-Based Hints
All hints expire automatically. When hints expire, endpoints return to baselineβa natural stability anchor.
4. Hard Constraints
Min/max intervals provide absolute limits. Even with volatile AI decisions, constraints bound the range.
Configuring for Volatile Workloadsβ
Use tighter constraint ranges for volatile data:
| Volatility Level | Min Interval | Max Interval | Ratio |
|---|---|---|---|
| Low (stable) | 10s | 10min | 60x |
| Medium | 30s | 5min | 10x |
| High | 30s | 2min | 4x |
| Extreme | 1min | 2min | 2x |
With a 4x range, even aggressive AI decisions stay bounded.
Requesting Stability in Descriptionsβ
Include phrases like:
- "Prioritize stability over responsiveness"
- "Don't overreact to momentary changes"
- "Look for sustained trends"
- "When uncertain, maintain current interval"
Response Design for Stabilityβ
Return smoothed/averaged values instead of instantaneous readings:
// Volatile (causes oscillation)
{ "current_value": 523, "instant_error_rate": 15.5 }
// Smoothed (promotes stability)
{
"avg_value_5min": 487,
"avg_error_rate_1hr": 2.3,
"trend": "stable",
"within_normal_range": true
}
Debugging Oscillationβ
If you observe oscillation:
- Check constraints: Are min/max intervals configured? Is the range appropriate?
- Review response data: Are you returning instantaneous vs. averaged metrics?
- Check AI analysis sessions: What reasoning is the AI providing?
- Add stability signals: Include
trend,within_normal_range, orrecommendationfields
Analysis Sessionsβ
Every analysis creates a session record with:
- Endpoint analyzed and timestamp
- Tools called (queries and actions)
- Reasoning (AI's explanation)
- Token usage and duration
- Next analysis time
- Endpoint failure count snapshot
Check sessions when an endpoint's schedule changes unexpectedly.
Quota and Cost Controlβ
AI analysis has quota controls:
- Per-tenant quota limits
- Before analyzing, check
quota.canProceed(tenantId) - If quota exceeded, skip analysis (Scheduler continues on baseline)
This ensures jobs keep running even if AI becomes unavailable or too expensive.
Failure Modes and Graceful Degradationβ
Cronicorn is designed to keep running regardless of what fails:
| Failure | What Happens | User Impact |
|---|---|---|
| AI Planner unavailable | Scheduler continues on baseline schedules. Existing hints remain until TTL expires. | Endpoints run normally, just without AI adaptation |
| AI quota exceeded | Analysis skipped for billing period. Scheduler unaffected. | Same as above |
| Endpoint returns non-2xx | Failure count increments. Exponential backoff applies (2^failures, cap at 32x). AI analyzes the failure pattern. | Endpoint slows down but doesn't stop |
| Endpoint times out | Recorded as failed. Same backoff as non-2xx. | Same as above |
| Response body empty/malformed | AI still sees HTTP status, duration, and health metrics. Gracefully handles any content. | AI makes decisions based on available data |
| Response body too large | Truncated to 500 characters. AI works with truncated data. | Design response bodies with key fields first |
| AI proposes invalid interval | Governor clamps to min/max constraints. | Constraints always enforced regardless of AI |
| Database connection lost | Both workers retry with backoff. No data lost. | Brief scheduling delay |
The system self-heals: When AI recovers, it resumes analysis. When endpoints recover (return 2xx), failure counts reset to 0 immediately. When hints expire, schedules revert to baseline. No manual intervention is required for any failure scenario.
Key Takeawaysβ
- AI controls its own analysis frequency
- Multi-window health data enables accurate recovery detection
- Sessions have constraints (max 15 tool calls)
- Four action tools: propose_interval, propose_next_time, pause_until, clear_hints
- Hints have TTLs: Auto-revert on expiration
- Interval hints override baseline: Enables adaptation
- Nudging provides immediacy: Changes apply within seconds
- Structure response bodies intentionally
- Sessions provide audit trail
- Quota controls costs: AI unavailable β jobs stop running
See Alsoβ
- System Architecture - High-level dual-worker design
- How Scheduling Works - Detailed Governor logic and constraint behavior
- Configuration and Constraints - Setting up endpoints effectively
- Coordinating Multiple Endpoints - Workflow patterns