Some work is announced by an event: a pull request is opened, an issue is assigned. Plenty is not. Nothing fires a webhook when your dependencies drift a month behind, when the README stops matching the code, or when a test has been quietly skipped since March.
A schedule loop is the trigger for that work. Same instruction, same run, same reviewable merge request — the clock just takes the place of the webhook.
One-time and recurring
- One-time — run an instruction once at a future moment. "Draft the release notes at 17:00 on Friday." It deactivates itself after firing.
- Recurring — run daily, weekly, or monthly at a time you choose.
Every loop carries its own timezone, so "09:00 weekly" means nine in the morning where your team actually is, and it keeps meaning that across daylight-saving changes.
A missed minute is skipped, not backfilled. If a loop could not fire — the platform was down, the schedule moved — the next occurrence runs normally rather than replaying every fire you missed at once.
Start from a template
You do not need to write a good instruction from a blank page. The create screen ships a gallery of starter loops, each with a cadence and a full instruction you then edit:
| Template | Cadence | What it does |
|---|---|---|
| Dependency upgrades | Weekly, Monday 08:00 | Safe patch and minor bumps, runtime and dev deps in separate MRs, tests run before opening |
| Test-suite health | Daily, 06:00 | Runs the full suite on the default branch; opens an MR if the fix is small and safe, otherwise reports |
| Docs drift | Weekly, Wednesday | Compares the README and docs against the last seven days of merges and fixes what is now wrong |
| Changelog / release notes | Weekly, Friday 16:00 | Opens an MR adding a CHANGELOG entry for the week's merges |
| Tech-debt sweep | Weekly, Monday | New TODO/FIXME and skipped tests — an MR for the safe ones, a list of the rest |
| Daily activity digest | Daily, 09:00 | A high-signal 24h summary posted to a Slack channel |
| Stale-MR reminder | Daily, 10:00 | Open MRs idle for three days or more, posted to Slack |
| Linear triage nudge | Daily, 09:00 | Comments on issues missing a label or an estimate |
| Reminder | Daily | A plain recurring nudge to Slack or email |
Templates use {placeholders} — {group/repo}, {#channel}, {team} — that you replace with your own. Every field stays editable after you pick one.
Four of them, worked through
The instructions are the interesting part. These are shipped verbatim; read them as models for your own.
Dependency upgrades — scoping a change that touches everything
In
{group/repo}, check for safe patch and minor dependency upgrades. Put runtime and dev deps in separate merge requests, run the test suite to verify, and open the MRs. No major-version bumps, no unrelated refactoring. If nothing is upgradable, do nothing.
Dependency work is where an unsupervised agent can do the most damage, so almost every clause here is a boundary. Separate merge requests means a broken dev dependency never blocks a security patch. Run the test suite to verify makes the goblin prove the bump before asking for your attention. No major-version bumps, no unrelated refactoring rules out the two ways this task classically sprawls — and it is worth noticing that the loop is more disciplined about scope than most humans doing the same chore on a Friday afternoon.
Test-suite health — deciding when to escalate instead of fix
Clone
{group/repo}, run the full test suite on the default branch. If something fails and the fix is small and safe, open an MR; otherwise summarise the failures. If all pass, do nothing.
This one encodes a judgement call rather than a procedure. A flaky assertion gets fixed; a genuine regression in payment handling gets reported, because "small and safe" is the goblin's own bar to clear and it is instructed to fall back to telling you rather than attempting a heroic fix at 6am. Loops are more useful when they know what they should not attempt.
Daily activity digest — teaching it what counts as signal
Summarise MRs merged/opened in
{group/repo}in the last 24h into a short, high-signal digest and post it to{#channel}withslack-send-message. High-signal only (merged, ready-for-review, unblocked, recurring CI failures); skip label/assignment churn and bot noise. If nothing meaningful happened, don't post.
A digest that reports everything is a digest nobody reads by week three. This instruction defines signal by enumerating both sides — what to include and what to drop — and then closes the loop with the clause that keeps it trustworthy: on a quiet day it stays quiet. An automation that posts "no activity today" every Sunday has already trained your team to ignore it.
Linear triage nudge — capping the blast radius
Review Linear issues in
{team}updated in the last day that are missing a label or estimate, and comment on each withlinear-commentasking for the missing detail. Inspect at most 30 issues; comment on at most 10. If none need attention, do nothing.
Note the two hard numbers. Without them, a loop pointed at a neglected backlog would work through six hundred issues on its first run and leave a comment on every one — technically correct, socially a disaster, and expensive. Inspect at most 30, comment on at most 10 keeps the first run indistinguishable from the hundredth. Anywhere a loop touches things people will see, put a number on it.
Writing an instruction that behaves overnight
Read those four again and the house style falls out. Unattended work deserves more explicit boundaries than a request you are sitting next to:
- Name the repository. A schedule loop has no issue and no thread to infer one from, so say
{group/repo}outright. - Cap the blast radius. "Touch at most a handful of files." "Comment on at most 10."
- Say what is out of scope. "No major-version bumps, no unrelated refactoring."
- Say when to report instead of act. "If the fix is small and safe, open an MR; otherwise summarise."
- Give it permission to do nothing. "If nothing is upgradable, do nothing." This is the single most valuable line in an overnight instruction — without it you get an empty merge request every Monday whether or not there was anything to change.
Anything durable — which repository is the default, how your tests are actually run, conventions the goblin should already know — belongs in organisation memory rather than repeated in every instruction. Keep the instruction about this loop.
Delivering when there is no thread to reply into
An event loop replies where the event happened. A schedule loop has nowhere obvious, so the instruction names the destination:
- Post to Slack — name a channel and the goblin posts there.
- Comment on a Linear issue — name the issue.
- Open a merge request — for anything that changes code, naming the repository explicitly.
If the instruction names nowhere, or something went wrong along the way, the goblin emails whoever created the loop rather than failing silently.
Each fire is independent
A recurring loop produces a steady stream of reviewable changes, not a growing pile of unattended commits. Every fire opens its own merge request and is reviewed like any other. Nothing merges without you.
Where to go next
- Event loops — the other trigger kind, for work your tools announce.
- Loops — the primitive both trigger kinds share.
- Automatic top-up — keeping the credit balance from stalling unattended work overnight.