AutomationCommercialFreeActiveMachine-verified· beginner · ~2 min setup

Claude Code /loop: Poll a Deploy on a Fixed Interval

Re-fire a prompt on a valid cron interval inside a live Claude Code session.

by Shilpa Mitra· verified today· v1.0.0

Run this workflow

CI-verified, 4/4 fixtures passing.

Intended Use

Anyone running Claude Code who wants to poll something (a deploy, a PR, a build) on a fixed interval inside a live session, with a cron expression that is valid before it is scheduled.

Not for

  • Schedules that must survive closing the terminal (use the headless or cloud setup instead)
  • Sub-minute intervals, cron granularity is one minute
  • Extended cron syntax like L, W, ? or MON/JAN aliases, which /loop does not support

The Stack

Tested Against

claude-code@2.1.72node@20.x

Side effects & data flow

Network
none, local only
Writes
./loopcron.mjs
Credentials
none required

Steps

  1. 1

    Schedule the poll

    Inside any session, /loop schedules a prompt to re-fire on an interval while the session stays open. Intervals use s, m, h, or d. For a fixed schedule it accepts standard 5-field cron.

  2. 2

    What CI checks: the cron expression is valid 5-field standard

    Before /loop schedules anything it needs a valid 5-field cron with no unsupported syntax. This validator accepts the post's examples and rejects extended syntax (L, W, ?) and name aliases (MON, JAN), exactly as the scheduler does. No model, no key.

    cat > loopcron.mjs <<'EOF'
    // Validate a 5-field standard cron the way Claude Code's /loop scheduler does.
    const ALIASES = ['MON','TUE','WED','THU','FRI','SAT','SUN','JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP','OCT','NOV','DEC'];
    const OK = new Set('0123456789*/,-'.split(''));
    function validate(expr) {
      const f = expr.trim().split(' ').filter(Boolean);
      if (f.length !== 5) return 'bad field count';
      for (const field of f) {
        const U = field.toUpperCase();
        if (U.includes('L') || U.includes('W') || U.includes('?')) return 'unsupported syntax';
        if (ALIASES.some(a => U.includes(a))) return 'unsupported alias';
        if (![...field].every(c => OK.has(c))) return 'illegal char';
      }
      return 'OK';
    }
    for (const g of ['0 9 * * 1-5', '*/15 * * * *']) {
      if (validate(g) !== 'OK') { console.log('FAIL valid rejected: ' + g); process.exit(1); }
      console.log('VALID: ' + g);
    }
    for (const b of ['0 9 L * MON']) {
      const r = validate(b);
      if (r === 'OK') { console.log('FAIL bad accepted: ' + b); process.exit(1); }
      console.log('REJECTED: ' + b + ' (' + r + ')');
    }
    console.log('loop cron check OK');
    EOF
    node loopcron.mjs
  3. 3

    Run the poll (the model step, not checked by CI)

    Once the cron is valid, the loop fires the prompt on each tick. That call invokes the model, so it is non-deterministic and fenced: CI never runs it and the badge never claims it.

Eval, 4 fixtures

Last passed: verified today
  • valid-acceptedcontainstimeout 30s · max $0

    Expected: VALID: 0 9 * * 1-5

  • bad-rejectedcontainstimeout 30s · max $0

    Expected: REJECTED: 0 9 L * MON

  • check-okcontainstimeout 30s · max $0

    Expected: loop cron check OK

  • clean-exitexit_codetimeout 30s · max $0

    Expected: 0

Results

The lightest automation tier: zero setup, dies with the session, 7-day auto-expiry.

Did this work for you?

Our CI checks the setup runs. You tell us if the whole thing worked. Tell us straight.

Liked this workflow?

Get new verified workflows in WebAfterAI, three issues a week (Tue, Thu, Sat).