Skip to content

Loops & Continuous Execution (loop) ​

While a function executes once in response to a direct trigger (like a network request or a button click), a loop is an autonomous, continuous workflow.

Loops are the engine behind your background workers, CRON jobs, stream listeners, and polling mechanisms. Just like functions, you do not write the implementation code. You define the trigger conditions and the permissions, and the Carotene compiler generates a strictly sandboxed, auto-scaling background process.

Defining the Trigger (config) ​

Because Carotene is infrastructure-aware, it needs to know how this background process should run. You define this using a typed config block. The compiler uses this block to provision the exact underlying infrastructure (e.g., AWS EventBridge, a detached Redis worker, or a Kafka consumer).

1. Scheduled Execution (CRON) ​

For tasks that need to run at specific intervals (like nightly billing or cleaning up expired sessions), you configure a cronloop.

dart
backend {
  // A background worker that runs autonomously
  loop AbandonedCartRecovery {
    config cron { schedule: "0 * * * *" } // Runs at minute 0 past every hour
    
    reads store.Cart
    calls integration.SendGrid.SendEmail
    updates store.Cart
  }
}

2. Event-Driven Execution (Streams & Listeners) ​

For real-time applications, you often need a worker to constantly listen for changes in your volatile state or listen to an external event stream.

dart
backend {
  // A worker that constantly processes a Redis queue
  loop MatchmakingWorker {
    config stream { listensTo: state.MatchmakingQueue }
    
    // The Sandbox permissions
    reads state.MatchmakingQueue
    creates store.GameSession
    mutates state.MatchmakingQueue
  }
}

The Sandbox & Verb Inheritance ​

The most powerful aspect of the loop primitive is that it uses the exact same Zero-Trust Permission Matrix as a function.

When the Carotene CLI hands your AbandonedCartRecovery loop to the AI code generator, the execution sandbox is built strictly from the verbs you provided.

  • The AI receives ctx.db.cart.read() and ctx.db.cart.update().
  • The AI receives ctx.SendGrid.SendEmail().

If the AI hallucinates and attempts to read the store.User table to personalize the email, the compiler instantly fails the build. If a permission is not explicitly granted at the architectural level, the background worker physically cannot execute it.

The Infinite Loop Guardrail (Execution Bounds) ​

AI coding agents (and human developers) are notorious for creating background workers that get trapped in infinite loops or memory leaks, bringing down production servers.

Carotene protects your infrastructure by enforcing Execution Bounds at the compiler level. When Carotene generates the runtime for a loop, it wraps the AI's implementation in a strict supervisor process.

By default, Carotene enforces the following guardrails on every loop:

  1. Strict Timeouts: If a single iteration of a cron loop takes longer than the default maximum (e.g., 5 minutes), the supervisor terminates the worker and logs a fatal error.
  2. Resource Limits: If a stream loop detects a memory spike characteristic of an unhandled while(true) hallucination, the supervisor kills the process and restarts it with a clean memory heap.

You can override these safety bounds explicitly in the config block if your architecture requires heavy, long-running batch processing:

dart
backend {
  loop HeavyVideoEncoding {
    config stream { 
      listensTo: state.EncodingQueue
      timeout: "2h" // Explicitly granting a 2-hour execution bound
      memoryLimit: "4GB"
    }
    
    reads store.VideoFile
    updates store.VideoFile
  }
}

By making infrastructure constraints declarative, you ensure that the AI excavator cannot accidentally deploy a worker that drains your cloud budget overnight.