feat(coding-agents): spread page refreshes with hashed cron fields (H) - #4208
Merged
Conversation
One `pageTriggerCron` is shared by every page in every bank running this
plugin, so a literal `"0 3 * * *"` does not schedule a refresh at 03:00 — it
schedules all of them at 03:00, ~5 page syntheses per bank, on the worker pool
that also serves retain. Moving the hour moves the pile.
Borrow Jenkins' `H`: a cron field written `H` is replaced, per page, by a value
hashed from bank + page name, so each page keeps its own stable slot.
"H H * * *" daily, at this page's own minute and hour
"H * * * *" hourly, at its own minute
"H 3 * * *" daily at 03:MM — spread inside the chosen hour
"H H(0-5) * * *" daily, spread across the night only
"0 3 * * *" unchanged
Spreading is a property of the schedule, so it lives in the expression rather
than in a new `pageTriggerType` member per period (`daily-staggered`, then
`hourly-staggered`, …), each of which would spell the period in the type name
and cost a config value, a branch and a docs row.
`H` never leaves the package: `expandCronHash` resolves it to an ordinary
5-field expression at page creation — the only point where the page's identity
exists — so the server parses plain cron and the control plane shows an
editable schedule. The field index is hashed alongside the seed so `H H * * *`
is worth 1440 slots rather than 60 correlated ones. A malformed `H` is refused
at config resolution, since `expandCronHash` deliberately leaves an expression
it cannot read alone rather than inventing a time nobody asked for.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Alternative to #4176, addressing the same problem with a different interface.
The problem (same as #4176)
One
pageTriggerCronis shared by every page in every bank running this plugin. A literal"0 3 * * *"therefore does not schedule a refresh at 03:00 — it schedules all of them at03:00, ~5 page syntheses per bank, on the worker pool that also serves retain. A session ingesting
at 03:0x queues behind the pile. Moving the hour moves the pile.
The interface
#4176 adds a
pageTriggerTypemember,"daily-staggered". That spells the period in the type name,so the next ask (hourly, or nightly-only) is another enum member, another branch, another docs row —
and
"daily-staggered"cannot express "spread within 03:00" at all.Spreading is a property of the schedule, so put it in the expression. A cron field written
Hisreplaced, per page, by a value hashed from bank + page name:
pageTriggerCron"H H * * *""H * * * *""H 3 * * *""H H(0-5) * * *""0 3 * * *"H, no hashingHis Jenkins' syntax for exactlythis problem, borrowed rather than invented so it is recognisable. No new config field, no new enum
member.
Implementation notes
Hnever leaves the package.expandCronHashresolves it to an ordinary 5-field expression, sothe API parses plain cron and the control plane shows an editable schedule.
seedPages,captureInitiative) rather than inbuildPageTrigger, because that is the only point where the page's identity exists — the triggeris built once per session for all of them.
H H * * *is worth 1440 slots rather than 60correlated ones (a test asserts all 24 hours are reached across 200 banks).
His refused at config resolution with a fallback toauto-refresh, matching how amissing
pageTriggerCronis already handled.expandCronHashdeliberately leaves an expression itcannot read alone rather than inventing a time nobody asked for.
Not included
#4176's second change — reconciliation patching only
tags_matchso a hand-edited schedule survives— is an independent fix and is left to that PR.
Validation
892 tests pass (67 files), package build,
tsc --noEmit, and./scripts/hooks/lint.shall green.skill/SKILL.md, the docs page and the docs-skill reference were regenerated from the README afterlint, per CLAUDE.md.