Orbit Premiere: Paul's editorial calls kept; no filler proposals

Avatar: Fire and Ash (then Orbit Sci-Fi) and Spider-Man: Brand New Day
(then Orbit Action) are approved Premiere debuts, both keeping Premiere
eligibility; Mayday is not a theatrical premiere (Apple Original Film,
Apple TV, 4 September 2026) and goes to regular Orbit Action. Called films
are never proposed again and count towards that week's three. Only films
over the bar are proposed, so a week can show fewer than three. Nothing is
booked or listed: the approved debuts wait for the booking workflow.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-11-05 15:19:27 +00:00
co-authored by Claude Opus 5.5
parent b17274c925
commit 01178ca8ec
3 changed files with 44 additions and 7 deletions
@@ -77,13 +77,15 @@ internal class DeskReport(val newlyReleased: Int, val newToLibrary: Int, val sca
/** This week's Orbit Premiere proposals, at most three. */
val premieres: List<DeskPremiere> = emptyList(),
/** Recent major films whose evidence is still to be read before they can be judged. */
val premiereQueued: Int = 0) {
val premiereQueued: Int = 0,
/** Premiere debuts you've approved, waiting for the booking workflow. */
val premiereApproved: List<String> = emptyList()) {
fun toJson(): JSONObject = JSONObject().put("newlyReleased", newlyReleased).put("newToLibrary", newToLibrary).put("scanned", scanned).put("unread", unread)
.put("strong", strong).put("strongByChannel", JSONArray(strongByChannel.map { JSONObject().put("c", it.first).put("n", it.second) }))
.put("picks", JSONArray(picks.map { JSONObject().put("t", it.title).put("c", it.channel).put("g", it.tag).put("l", it.line).item(it.id, it.type, it.year, it.options, it.firstRun) }))
.put("asks", JSONArray(asks.map { JSONObject().put("t", it.title).put("a", it.first).put("b", it.second).put("l", it.line).item(it.id, it.type, it.year, it.options, it.firstRun) }))
.put("collections", collections).put("franchises", franchises).put("catalogueComplete", catalogueComplete).put("computedMs", computedMs).put("tookMs", tookMs)
.put("decided", decided).put("premieres", JSONArray(premieres.map { it.toJson() })).put("premiereQueued", premiereQueued)
.put("decided", decided).put("premieres", JSONArray(premieres.map { it.toJson() })).put("premiereQueued", premiereQueued).put("premiereApproved", JSONArray(premiereApproved))
companion object {
fun of(o: JSONObject): DeskReport {
fun list(key: String) = o.optJSONArray(key)?.let { a -> (0 until a.length()).map { a.getJSONObject(it) } }.orEmpty()
@@ -92,7 +94,8 @@ internal class DeskReport(val newlyReleased: Int, val newToLibrary: Int, val sca
list("picks").map { DeskPick(it.optString("t"), it.optString("c"), it.optString("g"), it.optString("l"), it.optString("i"), it.optString("k"), it.year(), it.options(), it.firstRun()) },
list("asks").map { DeskAsk(it.optString("t"), it.optString("a"), it.optString("b"), it.optString("i"), it.optString("k"), it.year(), it.options(), it.optString("l"), it.firstRun()) },
o.optInt("collections"), o.optBoolean("catalogueComplete"), o.optInt("franchises"), o.optLong("computedMs"), o.optLong("tookMs"), o.optInt("decided"),
list("premieres").map(DeskPremiere::of), o.optInt("premiereQueued"))
list("premieres").map(DeskPremiere::of), o.optInt("premiereQueued"),
o.optJSONArray("premiereApproved")?.let { a -> (0 until a.length()).map { a.getString(it) } }.orEmpty())
}
}
}
@@ -228,7 +231,8 @@ internal fun buildDeskReports(evidenceFile: File, collectionsFile: File, pools:
DeskPick(p.evidence.name, p.top.name, when { p.tier == FitTier.Possible -> "SOMETHING DIFFERENT"; recent(p.evidence.id) -> "JUST RELEASED"; else -> "STRONG MATCH" },
p.evidence.overview?.let { sentence(it) } ?: "No description in the library yet", p.evidence.id, p.evidence.type, p.evidence.year, optionsOf(p))
}
DeskReport(candidates.size, newToLibrary, read.size, candidates.size - read.size, byChannel, strong.size, picks, asks, collections, catalogueComplete, franchises, nowMs, tookMs, placed.count { it.evidence.id in decided }, premieres, premiereQueued)
DeskReport(candidates.size, newToLibrary, read.size, candidates.size - read.size, byChannel, strong.size, picks, asks, collections, catalogueComplete, franchises, nowMs, tookMs, placed.count { it.evidence.id in decided }, premieres, premiereQueued,
PremiereCalls.filter { it.premiere }.map { it.title })
}
}
@@ -267,7 +271,8 @@ internal fun premiereInputs(store: ScheduleStore, catalogue: CatalogueStore, use
val booked = store.loadBooked(userId)
val weekStart = today.with(java.time.DayOfWeek.MONDAY).atStartOfDay(zone).toInstant().toEpochMilli()
return PremiereInputs(catalogue.premiereFacts(today.minusDays(PREMIERE_LOOKBACK_DAYS).toString(), today.toString()), { orbitStatus(it, booked, upcoming) },
loadDecisions(store.decisionsFile()).count { it.premiere && it.atMs >= weekStart })
loadDecisions(store.decisionsFile()).count { it.premiere && it.atMs >= weekStart } +
PremiereCalls.count { !it.decidedOn.isBefore(today.with(java.time.DayOfWeek.MONDAY)) && !it.decidedOn.isAfter(today) })
}
/** The first sentence of a description, kept short for a card. */
@@ -360,6 +365,7 @@ internal fun DirectorChair(load: (DirectorMode) -> DeskReport?, first: FocusRequ
onClick = { open = ChairChoice(p.title, p.id, "Movie", p.year, "ORBIT PREMIERE", (p.reasons + p.cautions.map { "but $it" }).joinToString("; "),
listOfNotNull(p.home), false, DeskOption(PREMIERE_NUMBER, "Orbit Premiere", p.after + if (p.remainsOnPremiere) ", and stays eligible for Premiere" else "")) })
}
if (r.premiereApproved.isNotEmpty()) Text("Approved for Premiere, waiting for booking: ${r.premiereApproved.joinToString(", ")}", color = ChairMint, fontSize = 10.sp)
if (r.premiereQueued > 0) Text("${r.premiereQueued} recent major films still being read for Premiere", color = Muted, fontSize = 10.sp)
val picks = r.picks.filter { it.id !in decided }
if (picks.isEmpty()) Text("Nothing stands out this week.", color = Muted, fontSize = 12.sp)
@@ -17,6 +17,21 @@ internal const val PREMIERE_WORTHWHILE = .6
/** Films released longer ago than this aren't proposed by the Chair; an older major film can still be chosen editorially. */
internal const val PREMIERE_LOOKBACK_DAYS = 730L
/**
* Paul's editorial calls on Premiere proposals, kept by library item: an approved debut with its permanent home (and whether it
* stays eligible for Premiere), or a ruling that a film isn't a theatrical premiere. Approved debuts wait here for the booking
* workflow, which shows their dates and collision checks before anything is booked; nothing is booked or listed from this.
*/
internal class PremiereCall(val itemId: String, val title: String, val premiere: Boolean, val home: Int, val keepsPremiere: Boolean, val note: String,
/** The day of the call: it counts towards that week's three. */
val decidedOn: LocalDate = LocalDate.of(2026, 11, 5))
internal val PremiereCalls = listOf(
PremiereCall("7789551", "Avatar: Fire and Ash", true, 303, true, "approved by Paul on 5 November 2026: Premiere debut, then Orbit Sci-Fi"),
PremiereCall("8006401", "Spider-Man: Brand New Day", true, 302, true, "approved by Paul on 5 November 2026: Premiere debut, then Orbit Action"),
PremiereCall("7974596", "Mayday", false, 302, false,
"not a theatrical premiere: an Apple Original Film released on Apple TV on 4 September 2026 (Paul, 5 November 2026); regular Orbit Action instead"))
/** What the library says about a film, from the catalogue: enough to judge its release profile before its evidence is read. */
internal class PremiereFacts(val id: String, val title: String, val year: Int?, val genres: Set<String>, val studios: List<String>, val premiere: String,
val runtimeMs: Long?, val work: String)
@@ -97,8 +112,11 @@ internal fun premiereCandidate(f: PremiereFacts, evidence: TitleEvidence?, commu
* This week's proposals: the strongest worthwhile candidates you haven't decided, at most [PREMIERE_PROPOSALS_PER_WEEK] less
* the premieres you've already decided this week. The rest wait; they're never shown as a list of decisions.
*/
internal fun weeklyPremiereProposals(candidates: List<PremiereCandidate>, decided: Set<String>, decidedThisWeek: Int): List<PremiereCandidate> =
candidates.filter { it.worthwhile && it.facts.id !in decided }.distinctBy { it.facts.work }.sortedByDescending { it.priority }
internal fun weeklyPremiereProposals(candidates: List<PremiereCandidate>, decided: Set<String>, decidedThisWeek: Int,
/** Films you've already ruled on ([PremiereCalls]): never proposed again, either way. */
called: Set<String> = PremiereCalls.map { it.itemId }.toSet()): List<PremiereCandidate> =
// Only films over the bar are proposed: a week with fewer than three worthwhile films shows fewer, never a filler.
candidates.filter { it.worthwhile && it.facts.id !in decided && it.facts.id !in called }.distinctBy { it.facts.work }.sortedByDescending { it.priority }
.take((PREMIERE_PROPOSALS_PER_WEEK - decidedThisWeek).coerceAtLeast(0))
/** A film's Orbit status: booked ahead on any channel ([upcoming]), booked before ([booked]) and not ahead, or neither. */
@@ -67,6 +67,19 @@ class PremiereSelectionTest {
assertTrue(weeklyPremiereProposals(all, setOf(week.first().facts.id), 0).none { it.facts.id == week.first().facts.id })
}
@Test fun yourCallsStickAndNoFillerIsMade() {
// Paul's calls: Avatar and Spider-Man approved, Mayday not a theatrical premiere. None is proposed again.
val called = PremiereCalls.map { it.itemId }
val films = called.map { id -> candidate(facts(id, "Called $id", listOf("Universal Pictures")), trending = true) }
assertTrue(weeklyPremiereProposals(films, emptySet(), 0).isEmpty())
assertEquals(setOf(303, 302), PremiereCalls.filter { it.premiere }.map { it.home }.toSet())
assertFalse(PremiereCalls.single { it.title == "Mayday" }.premiere)
// One worthwhile film and weaker ones: one proposal, not three.
val week = weeklyPremiereProposals(listOf(candidate(facts("31", "Big One", listOf("Pixar")), trending = true),
candidate(facts("32", "Small One", listOf("Tiny Films")), fit = .1, community = 5.0), candidate(facts("33", "Old One", listOf("Pixar"), premiere = "2022-01-01"))), emptySet(), 0)
assertEquals(listOf("31"), week.map { it.facts.id })
}
/** Hand-run on saved inputs: ORBIT_DIRECTOR_FIXTURE with premiere-facts.json, evidence-0.jsonl and the channels' episodes files. Writes premiere-selection.json. */
@Test fun replayOnTheLibrary() {
val dir = System.getenv("ORBIT_DIRECTOR_FIXTURE")?.let(::File)