Repository navigation
Lifting the repeat rules for a stretch of the day #30
Closed
robert-dean
started this conversation in
Ideas
Replies: 2 comments
|
Status check, 2026-09-22 (main at 23b983e) Only phase 3's narrow form has landed ( |
0 replies
|
Closing: all three phases are built, in #759.
|
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Status (2026-10-10)
The ask is still one sentence: an operator wants a stretch of the day where the repeat window and
the artist cooldown do not apply. What has changed since this was written is everything around it.
modeandonEndare built (2026-09-02), in bothSlotEditorand theon-air panel, each with copy saying a lift does not reach a dislike.
rulesAskedForindirector/director.console.service.tscopies flatPutOnAirInputfields (breaks,callins,mixInSimilar,chartPositions,requestShowand the three request limits) intoStationLineupRules. The schedule carries the same switches as nullable columns of their own(
0075_slot_chart_positions.sql,0079_slot_breaks.sql,0081_slot_request_show.sql), andScheduleTickJob.changeOvercopies each one intoputOnAir. That is the precedent the threerepeat fields should follow; see "Decisions" for how it changes the one this file made.
director/smart.shuffle.ts) leans every draw away from what aired recently throughfreshWeightin
rotation.rules.ts, on by default over a 14-day horizon, withFRESH_FLOORkeeping a just-airedrecord at a quarter of its weight rather than at zero. Rediscovery (Rediscovery: bring back liked artists, and play deep cuts #525,
director/rediscover.ts)then leans toward a liked artist gone quiet and toward deep cuts, and says in so many words that it
ranks by likes and never by play counts, because ranking by what aired is the feedback loop The station's judgement #37
warns about. A
CandidateTrack.playscount and a least-played weight would now be a third leananswering the same question with the signal Rediscovery chose not to use.
What is left is the lift itself:
repeatWindowDays,artistCooldownMinutesandmaxPerArtistaredeclared on
StationLineupRulesand applied byresolveRules, and nothing sets any of them.rulesAskedFordoes not copy them,PutOnAirInputhas no field for them, andschedule_slotshasno column for them. The only way to change how often the station repeats itself is still to change
it for the whole station in
rotation.*.Why the window has to be lifted at all
A Metallica hour is not blocked by anything that names Metallica. It is blocked by three rules that
each look reasonable on their own, and all three have to come off or the hour does not happen:
artistCooldownMinutesmaxPerArtistrepeatWindowDaysThree fields, not one switch, and that is the first thing to write down: an "ignore the repeat
window" checkbox alone changes nothing an operator would notice, because the cooldown is what is
actually refusing the records.
What is already in the tree, and what it costs to reach
resolveRulesis the whole evaluation path and it needs no change.rotation.rules.ts resolves tightest-last:
the mode's baseline (
stationfor arotation,NO_RULESfor asetlistor afeature), thenthe lineup's own overrides field by field. An overrule is an override, so this is already the code
that applies it. Nothing new judges anything, which keeps
docs/internals/director.md§ "Who ownsthe running order" intact: the schedule stays a document, a resolver and a timer that posts
commands.
The row already holds it.
station.lineup.repository.tsselects and writesrulesas jsonb, andStationLineup.rulesreads it back. It already carriesbreaks,callinsand the request switches forevery broadcast that asked for them.
The schedule slot carries everything except this.
deadair.schedule_slotshas grown a columnper switch (
breaks,request_show,chart_positions,moodsand the rest), andScheduleTickJob.changeOvercopies each of them intoputOnAir. The repeat fields would ride exactlywhere
briefandbreaksride and for the identical reason thebrief is on the row rather than in a job payload:
on_end = 'extend'keeps asking for more, soanything held only by the batch that started the block is gone within the hour with nothing saying
Decisions
The overrule is the existing per-field bag, on the slot and on
putOnAir. Not a new mechanism.Same shape, same resolver, copied onto the binding at changeover.
Columns, not a jsonb bag on the slot, and this reverses what this file first said. It argued for
one
rulesjsonb column onschedule_slotsmirroringstation_lineup.rules, on the grounds thatper-field columns define the shape twice and the next field would need a migration to reach the
schedule. The tree has since added three switches to the slot the other way (0075, 0079, 0081), each
a nullable column with a flat field on
PutOnAirInputand one line inrulesAskedFor. A fourthmechanism for three more fields would leave the slot holding some rules as columns and some in a bag,
which is worse than either. Follow the precedent.
mode: 'feature'is NOT the answer, even though it looks like one. Afeaturebaselines toNO_RULES, which does turn all three rules off, and also turns offbreaks,welcome,autoExtendandcrossfade. An operator who asked for a Metallica hour and got one with a silentDJ, no greeting for anyone tuning in, and a running order that stops when the batch does has been
answered with something they did not ask for. The overrides are per field precisely so this does not
have to be all-or-nothing, and
resolveRules' own comment already makes the argument in the otherdirection ("an operator who wants a cooldown inside a long setlist can have one").
A lift never reaches an instruction.
rejectDislikedstays, and so does anythingnever-play-rules lands: those are absolute by construction and live outside
ResolvedRulesfor that reason, so this change literally cannot reach them. Say it out loud in theconsole copy anyway, because "ignore the repeat rules" reads broader than it is.
Slot-scoped, not artist-scoped. There is no "always bypass the window for Metallica". A rule
about a THING is the ratings mechanism and a rule about a KIND of thing is
never-play-rules; this is a rule about a STRETCH OF THE DAY, which is the
scope the schedule already owns. An artist-scoped exception would be a fourth authority over what may
air, and it would be invisible on the grid.
The manual path gets it too. Three fields on
PutOnAirInput, so a spontaneous Metallica hour is possiblewithout editing the schedule. Without it, the only way to lift a rule is to add a block to the
timetable and wait for the boundary, which is not what an operator pressing "on air" means.
The phases
PutOnAirInputand throughrulesAskedFor. The.ckcontract, theSDKs it generates, and three more lines in
rulesAskedFor.resolveRulesalready applies themand the row already stores them, so this makes a lift expressible with no new evaluation
anywhere. Commit: a broadcast can be told which repeat rules to lift.
schedule_slots, the migration, the repository, the contract andchangeOvercopying them into
putOnAirbesidebreaks. Nothing sets them from the console yet, so theschedule behaves exactly as it does today. Commit: a slot carries its own repeat rules.
three in
SlotEditorand on the on-air panel, with the same copy the mode field already carriesabout a lift never reaching a dislike. Commit: an operator can lift the repeat rules for a block.
Each stands alone and 1 is usable from the API and the SDKs before 2 and 3 land.
What the lift does to smart shuffle
Nothing, and that is worth saying before somebody assumes it should. Smart shuffle is a lean and
never a refusal, so it is not one of the three rules a one-artist hour has to lift. With the window
and the cooldown lifted it is what stops a small pool playing the same four records on a loop: a
record that aired twenty minutes ago is drawn at about a quarter of the weight of one that has
rested. If a themed hour ever wants to come round faster than that,
FRESH_FLOORis the knob, and itis a station-wide one today; making it a fourth per-broadcast field is a separate decision and not
part of this.
What stops a record coming round twice in one batch
What stops an hour repeating a record inside its own batch is not the window, and this is worth
knowing before assuming the lift breaks something:
SetGeneratorChaindedupes bysongKeyacrossthe batch and against
songKeysOfthe remaining order. So lifting the window does not produce abatch holding one record twice. What it produces is a record coming round again a few batches later,
which is the entire point, and what smart shuffle then shapes.
Related
mode, schedule slot) and for the bias-versus-veto split this file inherits.
argument about a stale draw and is deliberately a different mechanism.
2026-08-19.
docs/internals/director.md§ "Who owns the running order" for why the schedule may neverevaluate a rule itself.
All reactions