From 9bf56cbb4e72aff1022af6ad0b7cb4d453a99ccc Mon Sep 17 00:00:00 2001 From: prosolis <5590409+prosolis@users.noreply.github.com> Date: Sat, 11 Jul 2026 07:44:15 -0700 Subject: [PATCH] Adventure news: add zone_clear event type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gogobee now splits the realm's first-ever clear (zone_first, priority) from a later repeat (zone_clear, bulletin) so a repeat is never filed as a first-ever. Handle both: - renderAdventure: zone_first keeps the "cleared for the very first time" headline; zone_clear gets the personal "{subject} clears {zone}" headline, sharing the lede. A tier fallback still handles a legacy zone_first that predates the split. - advEventMeta: zone_clear -> "Zone cleared" (distinct from zone_first's "First clear"), so a repeat's permalink page and emblem aren't stamped "First clear". The GUID contract is otherwise unchanged: prefix == event_type, so the permalink label derives correctly. Test: TestRenderZoneTaxonomy. Deploy this before the matching gogobee change β€” a zone_clear fact hits an un-updated Pete as an unknown event_type (400). --- internal/web/adventure.go | 11 ++++++++--- internal/web/adventure_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/internal/web/adventure.go b/internal/web/adventure.go index 3da3965..b8821df 100644 --- a/internal/web/adventure.go +++ b/internal/web/adventure.go @@ -153,6 +153,8 @@ func advEventMeta(eventType string) (label, emoji string) { return "Boss down", "πŸ‰" case "zone_first": return "First clear", "πŸ—ΊοΈ" + case "zone_clear": + return "Zone cleared", "πŸ—ΊοΈ" case "death": return "In memoriam", "πŸͺ¦" case "arrival": @@ -328,9 +330,12 @@ func renderAdventure(f AdvFact) (headline, lede string, ok bool) { case "boss_kill": return fmt.Sprintf("%s takes down %s again.", f.Subject, f.Boss), fmt.Sprintf("Another clean run in %s today. Routine for %s by now β€” but still worth a nod.", f.Zone, f.Subject), true - case "zone_first": - // Realm-first (priority) vs personal (bulletin) share a lede. - if f.Tier == "priority" { + case "zone_first", "zone_clear": + // gogobee splits the realm's first-ever clear (zone_first, priority) from a + // later repeat (zone_clear, bulletin); they share a lede but differ in + // headline. Fall back to the tier for a legacy zone_first that predates the + // split. + if f.EventType == "zone_first" || f.Tier == "priority" { headline = fmt.Sprintf("%s cleared for the very first time.", f.Zone) } else { headline = fmt.Sprintf("%s clears %s.", f.Subject, f.Zone) diff --git a/internal/web/adventure_test.go b/internal/web/adventure_test.go index 2ade317..c55bcfc 100644 --- a/internal/web/adventure_test.go +++ b/internal/web/adventure_test.go @@ -194,6 +194,30 @@ func TestAdventureDigest(t *testing.T) { // TestAdventureArtAndMeta covers the visual-identity slice: the emblem endpoint // returns a themed SVG, ingested cards carry its local path, and the permalink // page is noindex with an og:image. +// TestRenderZoneTaxonomy: a realm-first (zone_first) reads as first-ever; a +// repeat (zone_clear) reads as a personal clear, not a mis-labeled "first". +func TestRenderZoneTaxonomy(t *testing.T) { + first := AdvFact{EventType: "zone_first", Tier: "priority", Subject: "Brannigan", + Zone: "Dragon's Lair", Region: "the Underforge", Level: 14} + hl, _, ok := renderAdventure(first) + if !ok || !strings.Contains(hl, "very first time") { + t.Errorf("zone_first headline = %q (ok=%v)", hl, ok) + } + + repeat := AdvFact{EventType: "zone_clear", Tier: "bulletin", Subject: "Brannigan", + Zone: "Dragon's Lair", Region: "the Underforge", Level: 14} + hl2, _, ok := renderAdventure(repeat) + if !ok || !strings.Contains(hl2, "Brannigan clears") || strings.Contains(hl2, "first") { + t.Errorf("zone_clear headline = %q (ok=%v)", hl2, ok) + } + + // The permalink label distinguishes the two, so a repeat's page isn't stamped + // "First clear". + if lbl, _ := advEventMeta("zone_clear"); lbl == "First clear" { + t.Errorf("zone_clear meta label = %q, want distinct from first clear", lbl) + } +} + func TestAdventureArtAndMeta(t *testing.T) { const token = "t" s, _ := newAdvServer(t, token)