Skip to content

fix(universal): stop reporting shows as OPERATING outside park hours - #321

Draft
cubehouse wants to merge 2 commits into
mainfrom
fix/universal-show-clock-gate
Draft

fix(universal): stop reporting shows as OPERATING outside park hours#321
cubehouse wants to merge 2 commits into
mainfrom
fix/universal-show-clock-gate

Conversation

@cubehouse

@cubehouse cubehouse commented Aug 19, 2026

Copy link
Copy Markdown
Member

Summary

Universal show entities were reading OPERATING straight through the overnight
park closure and never getting rewritten, so the last-known status froze.

Sampled overnight with the park's own venue schedule confirming it shut, most
externally-shown show entries carried a live-looking status (either an
explicit OPEN, or CLOSED/CANCELED with a future showtime still listed —
show_times lists the whole day's slots from midnight, so "has a future
performance" never goes false overnight). Neither signal referenced park
hours at all.

Show status is now clock-gated against the park's own venue schedule the same
way #317 did for SeaWorld: OPERATING (from either an explicit
OPEN/RIDE_NOW or a future showtime) only holds while the park is actually
open. Delay and long-closure signals are untouched — neither claims
OPERATING, so there's nothing to gate.

Hardening after review

This branch went through three independent reviews (crash/correctness risk,
real-world regression risk, test coverage) before being opened as a PR. They
found real gaps, now fixed:

  • isParkOperatingNow could throw on a malformed-but-successful schedule
    response (a non-array body), taking down buildLiveData for the whole
    destination rather than degrading gracefully. Validated and wrapped end to
    end now.
  • A schedule with zero usable days (empty array, every day unparseable) was
    treated identically to a confirmed closure. Now distinguished from a
    schedule that explicitly marks every day Closed (a real signal — e.g.
    Volcano Bay's off-season) — only the former fails open (ungated).
  • buildSchedules assumed OpenTimeString/CloseTimeString are always
    present; real UOR data omits them on some days. Fixed the same way (skip
    rather than publish an Invalid Date).
  • Added missing coverage: Universal Orlando's 4-park case (previously
    untested — only single-park Hollywood was exercised), the venue
    reparenting branch end to end, the unmapped-venue fail-open branch,
    multi-day schedule scanning, and non-array/empty-array/all-closed
    response shapes.

Known residual risk — not fixed, flagged for follow-up

The type declared a SpecialEntryString field that isn't real; the actual
field is SpecialEntryUnix, present on every real day captured but 0 in
every sample checked across ~11 weeks (both resorts), including deep into a
would-be Halloween Horror Nights window. Its real meaning is unconfirmed —
possibly unused by this endpoint entirely, possibly only populated close to
an actual special-hours date. Left deliberately unread rather than guessing
at undocumented semantics.

The open question this leaves: does the legacy /venues/{id}/hours
endpoint this fix relies on cover ticketed after-hours events (e.g. HHN) at
all?
No HHN-branded shows exist in the feed yet (event is weeks out), so
this can't be resolved from current data. If such an event's hours aren't
covered by this endpoint, shows tied to it would be wrongly gated CLOSED
during the live event — the opposite failure mode from the one this PR
fixes. Worth a live check once such an event is actually running; will file
a follow-up card for that.

Verification

  • Full npm test suite green (2066 tests).
  • npx tsc --noEmit clean.
  • Live-verified against both US resorts while genuinely closed (~00:26 PDT /
    03:26 EDT, then re-verified after the hardening pass): Universal Studios
    Hollywood's 45 live rows and Universal Orlando's 96 live rows across all
    four parks are all CLOSED.

Test plan

  • npm test — full suite passes
  • npm run build — typechecks clean
  • npm run dev -- universalstudios and npm run dev -- universalorlando against live upstream while both resorts are closed
  • Three independent reviews (correctness, regression risk, test coverage) — findings addressed above
  • Cube review

🤖 Generated with Claude Code

cubehouse and others added 2 commits August 19, 2026 07:32
Sampled overnight with the park's own schedule confirming it shut, 25 of
31 externally-shown USH entries carried a live-looking show status ("OPEN",
or CLOSED/CANCELED with a future slot still listed) straight through the
closure. show_times lists the whole day's slots from midnight, so "has a
future performance" never goes false overnight, and the status field
itself isn't reliably live either — the same category of stale-reading bug
already fixed for the ride wait-time feed, just on shows.

Show status is now clock-gated against the park's own venue schedule the
same way: OPERATING (from either an explicit OPEN/RIDE_NOW or a future
showtime) only holds while the park is actually open. Delay and
long-closure signals are untouched — neither claims OPERATING, so there's
nothing to gate. A schedule lookup failure, or a venue with no schedule
(CityWalk), falls back to the old ungated behaviour rather than guessing.

Verified live at 00:26 PDT / 03:26 EDT with both US resorts shut: USH
went from 25 shows frozen OPERATING to all 45 rows CLOSED, and Orlando's
96 rows are CLOSED across all four parks.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…edge cases

Three independent reviews of the clock-gate fix (crash risk, real-world
regression risk, test coverage) surfaced concrete gaps before merge:

- isParkOperatingNow could throw on a malformed-but-successful schedule
  response (non-array body), taking down buildLiveData for the whole
  destination rather than degrading gracefully. Now validated and wrapped
  end to end.
- A schedule response with zero usable days (empty array, every day
  unparseable) was treated the same as a confirmed closure. Now
  distinguished from a schedule that explicitly says every day is closed
  (a real signal, e.g. Volcano Bay's off-season) — only the former fails
  open.
- The type declared a `SpecialEntryString` field that isn't real; the
  actual field is `SpecialEntryUnix`, present on every real day but 0 in
  every sample checked across ~11 weeks including a would-be Halloween
  Horror Nights window. Left deliberately unused rather than guessing at
  undocumented semantics — noted in the code as a known gap.
- buildSchedules assumed OpenTimeString/CloseTimeString are always
  present; real UOR data omits them on some days. Fixed the same way
  (skip, don't publish an Invalid Date).
- Added the missing coverage: Universal Orlando's 4-park case (previously
  untested — only single-park Hollywood was exercised), the venue
  reparenting branch end to end, the unmapped-venue fail-open branch,
  multi-day schedule scanning, and the non-array/empty-array/all-closed
  response shapes.

Full suite (2066 tests) green, typechecks clean, re-verified against both
US resorts live while genuinely closed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@cubehouse
cubehouse marked this pull request as draft August 19, 2026 07:53
@cubehouse

Copy link
Copy Markdown
Member Author

Holding this in draft. Found a real, confirmed risk after opening this PR: Halloween Horror Nights runs at both Universal Orlando and Universal Studios Hollywood as a wholly separate ticketed admission (day ticket ≠ HHN ticket, either direction), and the legacy /venues/{id}/hours schedule endpoint this fix clock-gates against shows zero signal of that evening window — confirmed against real data for 2026-08-28, Orlando's actual first HHN night this year (already locked into the schedule, only 9 days out at time of writing).

Unresolved: whether HHN-branded shows appear in show-list.json under the same day-park venue_id this fix gates. If they do, this fix would wrongly force them CLOSED during the live event.

Verifying live once HHN opens (~2026-08-28) rather than merging on a guess. Tracked as a gate on the internal board; will update here once resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant