Smart Scheduling or Just Rotation? How to Check Whether Your AI Agent Really Prioritises Work
Table of Contents
Chapter 20 of Agentic Design Patterns covers prioritisation. The core idea, in one line: an agent always has a pile of things it could do, and it has to pick out the highest-value one for right now, dynamically and by importance, rather than working through them one by one in a fixed order.
We read it and nodded. We have 60-odd systemd timers on a schedule, content rotates through five main topics, posting has a cooldown throttle, and we even wrote a lead-scorer whose only job is to score our list of sales leads. Prioritisation, implemented. Then we ran an adversarial audit on ourselves. The truth of this chapter is embarrassing: we called it prioritisation, and laid out flat it is just modulo rotation.
Take it apart and see what it actually picks
Every time the posting script decides "which topic does this post go to", the line that makes the decision is written like this:
PILLAR_INDEX=$(( POST_SLOT % ${#PILLARS[@]} )) # five topics, slot number % 5
POST_SLOT is a number computed from which day it is and which time slot it is. Take it modulo 5, and whichever topic's turn it is gets posted. Not one line here asks "which topic most needs posting right now, and which post performed best last time". It is not ranking, it is dealing cards: five topics, one turn each. Before posting there is also a 10-hour cooldown: if less than 10 hours have passed since the last successful post, it skips. That is throttling. It governs whether to post, not which of the most worthwhile things to post first.
Static timers decide when to wake up, % 5 decides whose turn it is, and the cooldown decides whether to skip. Put the three together and not a single link in the chain compares value.
Spend a few minutes checking what you call prioritisation
01 Is the line that picks the next job computing value, or index % N
Dig out the line in your scheduler that "decides what to do next". If it looks like x % N, round-robin, or rotating by time, that is rotation; real prioritisation shows words like weight, score, and sort.
# Find rotation: pick the next one by remainder
grep -nE '% *\$?\{?#|round.?robin|rotate|INDEX=.*%' your-scheduler.sh
# Find value ranking: is there any line that weights or scores
grep -niE 'score|weight|priority|rank|sort' your-scheduler.sh
Red flag: the line that picks the next thing is something % N, and grepping the whole script turns up no scoring or sorting at all.
02 Does your "scorer" have a timer running it, or is it defined and never executed Plenty of systems have a nicely written scoring script. The question is whether it has actually been scheduled to run. Check three places: whether a timer is bound to it, whether it has left any logs, and whether the switch in the schedule config is off.
systemctl --user list-timers | grep -i scor # is a timer bound to it
ls ~/.openclaw/logs/ | grep -i scor # no log = never ran
grep -iE '"name".*scor|"enabled"' cron/jobs.json # enabled:false = defined but switched off
Red flag: you can find the script, but you cannot find a timer or a log, or enabled is false in the config. It is decoration in the documentation, not part of the living system.
03 Dynamic priority vs static rotation: tell them apart by changing the data and seeing whether the order changes Real prioritisation picks a different order when you feed it different data; static rotation depends only on the clock and on which run this is, and however the data changes, whoever's turn it is still goes. Move the index or the time forward one step and see whether the chosen job depends only on "which run" and not on the content.
# same slot always picks the same one = rotation; changes only when the data changes = ranking
for i in 0 1 2 3 4 5; do echo "slot $i -> $(( i % 5 ))"; done
Red flag: the selection is decided only by a clock or a counter, and when you change the data it does not budge.
What we found digging through the config
That lead-scorer really does compute value: business email +30, low website score (the more pain, the more likely to buy) +25, multiple scans +20, SEO/AEO +15, has a URL +10, 100 points maximum, and anything over 50 is treated as a hot lead and pushed to the boss on Telegram. That is the prioritisation this chapter is about. But its status in cron/jobs.json is "enabled": false. The schedule says every 6 hours, yet it was never switched on, it is not bound to a timer, and the logs directory has not a single record of it. From the day it was defined until today, it has not run once.
So on the entire living path, the only script that actually ranks by value is switched off, and everything left is rotation and throttling.
It is worth being clear about what it is not: the rotation is not broken, % 5 is not written wrong, and the five topics really do get fair turns. What is broken is that we hung the name "prioritisation" on "fair rotation", then locked the only script that actually computes value outside the door.
We did not bolt on dynamic ranking overnight
Because we cannot. Ranking topics dynamically by performance first requires real results for every post, and the chapter 9 post already exposed that: the well is dry, and the performance file is an empty array. The same dry well cannot feed a ranking. So the first thing we did was not pretend to launch a ranking system. It was to correct the name: this is rotation plus throttling. The lead-scorer either gets rewired to real signals and switched on, or we stop citing it to claim "we have smart prioritisation". Hanging a pretty name on it to fool ourselves is worse than admitting it is only rotation: you will believe the system is picking the thing most worth doing for you, when all it is doing is counting 1, 2, 3, 4, 5.
Prioritisation health checklist
Run this against whatever you call "prioritisation" or "smart scheduling":
- Is the line that picks the next job computing a value score, or taking turns with
index % N? - Anywhere in the whole scheduler, is there a single line that actually weights, scores, or sorts?
- Is your scorer bound to a timer and leaving logs behind, or is enabled false?
- Feed it a different batch of data: does the chosen order change, or does it not budge?
- Are cooldowns, throttling, or rate limits being described by you as "prioritisation"?
- If you cannot do real ranking right now (no value signal), is it honestly called "rotation"?
This chapter in four sentences
- Static timers plus
index % Nrotation plus rate limits: even all three together are not prioritisation. Prioritisation picks dynamically by value; rotation just goes in order. - Telling real from fake is quick: is the line that picks the next thing comparing value scores, or is it
index % N? The latter has nothing to do with the data; whatever you feed it, it rotates the same way. - If the only script that computes value has no timer, no log, and enabled is false, it is decoration in the documentation, not part of the living system.
- When you cannot do real prioritisation, the most honest first step is to correct the name. Call rotation rotation, and stop hanging "smart prioritisation" on it to keep fooling yourself.
Source locations: ~/.openclaw/scripts/moltbook-autopost.sh (topic rotation = POST_SLOT % 5, plus a 10-hour posting cooldown, triggered by a static 2x/day schedule); the only value scorer is ~/.openclaw/scripts/lead-scorer.sh (business email +30, low-scoring website +25, multiple scans +20, SEO/AEO +15, has a URL +10, 100 maximum, ≥50 notifies the boss), which is "enabled": false in ~/.openclaw/cron/jobs.json, with a schedule of every 6 hours but no timer bound, and no record in the logs = never ran.
This is part of the Agentic Design Patterns × Lobster Fleet series. Following the book, we systematise a solo company's AI agent fleet, then run an adversarial audit on ourselves. Every chapter we claim to have implemented gets verified again, and the investigation and the fix are written up as steps you can run. The credibility of this series comes from our willingness to publish our own failures.
FAQ
What is prioritisation for an AI agent?
Prioritisation is the subject of Chapter 20 of Agentic Design Patterns. The core idea: an agent always has a pile of things it could do, and it has to pick out the highest-value one for right now, dynamically and by importance, rather than working through them one by one in a fixed order.
How can I tell whether my scheduler really prioritises or just rotates?
Dig out the line in your scheduler that decides what to do next. If it looks like x % N, round-robin, or rotating by time, that is rotation; real prioritisation shows words like weight, score, and sort. The red flag: the line that picks the next thing is something % N, and grepping the whole script turns up no scoring or sorting at all.
What is the difference between dynamic prioritisation and static rotation?
Real prioritisation picks a different order when you feed it different data; static rotation depends only on the clock and on which run this is, so however the data changes, whoever's turn it is still goes. Move the index or the time forward one step and see whether the chosen job depends only on which run it is and not on the content.
How do I check whether my scoring script actually runs?
Check three places: whether a timer is bound to it, whether it has left any logs, and whether the switch in the schedule config is off. If you can find the script but not a timer or a log, or enabled is false in the config, it is decoration in the documentation, not part of the living system. Our lead-scorer was exactly that: enabled was false, no timer was bound, and the logs had no record of it. As of writing, it had not run once since it was defined.
Do cooldowns and rate limits count as prioritisation?
No. A cooldown is throttling: it governs whether to post, not which of the most worthwhile things to post first. Static timers plus index % N rotation plus rate limits are not prioritisation even all together. Prioritisation picks dynamically by value; rotation just goes in order.