feat: per-day subEvent JSON-LD for multi-day events (#542 PR-4)#634
Merged
Conversation
Multi-day events (end_date > date) gain a subEvent array in their MusicEvent JSON-LD — one MusicEvent per festival day with that day's performers and dates, grouped by performance_date with the after-midnight convention (a 1 AM set belongs to the previous evening's day). Single-day events are byte-identical (no subEvent). Folds in two latent bugs the design pass found in the same file: - reveal_mode gate: JSON-LD now filters to announced sets on reveal-mode events (matching schedule.js/ical.js/timeline.js), so unannounced bands never leak into crawler-facing structured data. - hardcoded -04:00: startDate now uses torontoUtcOffset() (ICU longOffset probe, DST-correct) so winter events emit -05:00, not a year-round EDT offset. Additive-only and revertable as a single file; needs Google Rich Results validation before merge (SEO-facing). Closes #542 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
BreakableHoodie
marked this pull request as draft
July 18, 2026 17:46
Google requires `location` on every Event node, and the Rich Results Test validates nested subEvents too — so a subEvent without it is a "Missing field 'location'" failure, exactly what this PR is gated on. Hoist the location computation above the subEvent builder and spread it (plus the recommended eventStatus/eventAttendanceMode) onto each day. Also correct the "local noon" comment (the probe is noon UTC = 07:00-08:00 Toronto) and pin subEvent name+location in the test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Owner
Author
|
Addressed Vera's review (commit pushed):
Still DRAFT / held for your Google Rich Results Test on a live multi-day URL before merge. 801 backend tests green. |
This was referenced Jul 18, 2026
BreakableHoodie
marked this pull request as ready for review
July 18, 2026 20:34
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
BreakableHoodie
added a commit
that referenced
this pull request
Jul 18, 2026
Turns three of the repo's prose-and-agent-guarded invariants into deterministic regression tests (additive only, zero production code touched): - frontend after-midnight threshold: AFTER_MIDNIGHT_THRESHOLD_HOUR === 6, the derived MINUTES constant stays consistent, and bandUtils.js still imports the constant rather than re-encoding 6 (#550 bug class). - SQLite datetime format: toSqliteDateTime()'s output is space- separated with no T/Z/ms — the exact shape whose T-separator caused the SEC-F1 invite-expiry bypass. Tested through the real checkAuthRateLimit path since the helper isn't exported. - JSON-LD golden snapshot of /event/[slug]: a pinned, deterministic event (fixed created_at so offers.validFrom is reproducible) locks the crawler-facing MusicEvent + BreadcrumbList shape. Any future edit to that block — which churned across #615/#616/#634 this week — now surfaces as a reviewable snapshot diff; the location field Vera hand-caught can no longer silently regress. Intentional changes land via `vitest -u` + snapshot review, never blind. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.
This rewrites crawler-facing structured data (design risk 7/10). Before merge, validate a real multi-day event URL (e.g. Buddies Fest 2 once deployed to preview) in Google's Rich Results Test and confirm the
subEventrenders cleanly without regressing existing MusicEvent rich-result eligibility. The change is additive-only (single-day output is byte-identical, pinned by a regression test), so rollback is a clean single-file revert.Summary
Multi-day events (
end_date > date) gain asubEventarray in their MusicEvent JSON-LD — one MusicEvent per festival day, carrying that day's performers and dates. Performances are grouped byperformance_dateusing the after-midnight convention (a set before 06:00 belongs to the previous evening's day — verified: a 1 AM set buckets into Day 1, not Day 2). Single-day events get nosubEventand render byte-identically. Builds additively on the #616 (poster image) and #617 (dedup + enrich) MusicEvent work — none of those fields change.Closes #542 (final sub-PR; PR-1 staleness #604, PR-2 day-aware ICS, and PR-3 day tabs are the siblings)
What changed
functions/utils/eventDay.jstorontoUtcOffset(dateStr)— ICUlongOffsetprobed at local noon (DST-unambiguous), falls back to-05:00on bad inputfunctions/event/[slug].jsperformance_date/start_time+ gains the reveal_mode gate; top-level + per-subEventstartDateusetorontoUtcOffset()functions/event/__tests__/slug.test.jsend_date===dateedge, reveal-mode gating (top + sub), after-midnight bucketing, summer/winter offsetTwo folded latent bugs (both in this file, which the PR rewrites anyway):
-04:00— replaced with the DST-correct offset, so February events emit-05:00.Security / correctness notes
The reveal_mode gate is a latent info-disclosure fix (unannounced lineup leaking to crawlers). Scope note / follow-up: the separate venues query still has no reveal_mode gate — a venue whose only performance is unannounced could appear in
locationon a reveal-mode event. Left out to keep this SEO PR a single clean revertable commit; filed as a follow-up.Verification
Built by Sonny · Reviewed by Vera (pending) & Theo · 🤖 Claude Code