From be66af1ad57b57c992d136f61b87d4ad1e5ff301 Mon Sep 17 00:00:00 2001 From: jedarden Date: Wed, 22 Apr 2026 20:08:49 -0400 Subject: [PATCH] =?UTF-8?q?fix(narrative):=20add=20season=20championship?= =?UTF-8?q?=20context=20and=20=C2=A713.2=20critical=20moments=20to=20spotl?= =?UTF-8?q?ight=20prompt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add formatSeasonChampionshipContext helper to inject season progress, championship bracket positioning, and seed lines into LLM prompts. Add §13.2 critical moment / turning point summary to the match-of-the-week section of buildSpotlightPrompt. These complete the §15.1/§15.5 alignment for structured contextual match data in sports-journalism-style prompts. Co-Authored-By: Claude Opus 4.7 --- cmd/acb-index-builder/blog.go | 72 +++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/cmd/acb-index-builder/blog.go b/cmd/acb-index-builder/blog.go index 0f9099c..14de339 100644 --- a/cmd/acb-index-builder/blog.go +++ b/cmd/acb-index-builder/blog.go @@ -792,9 +792,12 @@ func buildSpotlightPrompt(data *IndexData, movers []eloMover, strats []strategyC sb.WriteString("Reference specific bot names, ELO deltas (before/after), rivalry dynamics, and critical moments. ") sb.WriteString("Be dramatic but factual. Write in present tense with a punchy, journalistic tone. Do not use emojis.\n\n") - // Season standings context - sb.WriteString(fmt.Sprintf("Season: %s\n", getCurrentSeasonName(data))) - sb.WriteString(fmt.Sprintf("Active bots: %d, Matches this week: %d\n\n", len(data.Bots), countWeeklyMatches(data))) + // Season standings context with championship positioning + seasonName := getCurrentSeasonName(data) + sb.WriteString(fmt.Sprintf("Season: %s\n", seasonName)) + sb.WriteString(fmt.Sprintf("Active bots: %d, Matches this week: %d\n", len(data.Bots), countWeeklyMatches(data))) + sb.WriteString(formatSeasonChampionshipContext(data)) + sb.WriteString("\n") // Season standings (top 5 with rank, rating delta, archetype) sb.WriteString("Season standings (top 5):\n") @@ -864,13 +867,27 @@ func buildSpotlightPrompt(data *IndexData, movers []eloMover, strats []strategyC if bestMatch != nil { sb.WriteString(fmt.Sprintf("\nMatch of the week: %s — score %s in %d turns [match %s]\n", bestMatch.Description, bestMatch.Score, bestMatch.TurnCount, bestMatch.MatchID)) - // Include pre-match ELO for participants if available + // Include pre-match ELO and §13.2 critical moment summary for participants for _, m := range data.Matches { if m.ID == bestMatch.MatchID && len(m.Participants) >= 2 { for _, p := range m.Participants { sb.WriteString(fmt.Sprintf(" %s: pre-match ELO %.0f\n", getBotName(p.BotID, data), p.PreMatchRating)) } + // §13.2 critical moment / turning point summary + var winner, loser *ParticipantData + for i := range m.Participants { + if m.Participants[i].Won { + winner = &m.Participants[i] + } else { + loser = &m.Participants[i] + } + } + if winner != nil && loser != nil { + if cm := summarizeCriticalMoment(m, winner, loser); cm != "" { + sb.WriteString(fmt.Sprintf(" Turning point: %s\n", cm)) + } + } break } } @@ -2199,6 +2216,53 @@ func buildBotSpotlight(data *IndexData) *botSpotlight { // ─── Formatting helpers (meta report specific) ──────────────────────────────── +// formatSeasonChampionshipContext returns a human-readable summary of season progress +// and championship bracket positioning for LLM prompts. +func formatSeasonChampionshipContext(data *IndexData) string { + var active *SeasonData + for i := range data.Seasons { + if data.Seasons[i].Status == "active" { + active = &data.Seasons[i] + break + } + } + if active == nil { + return "" + } + + daysElapsed := data.GeneratedAt.Sub(active.StartsAt).Hours() / 24 + weekNum := int(daysElapsed/7) + 1 + if weekNum > 4 { + weekNum = 4 + } + + var sb strings.Builder + theme := "" + if active.Theme != "" { + theme = fmt.Sprintf(" (%s)", active.Theme) + } + sb.WriteString(fmt.Sprintf("Season progress: Week %d of 4%s", weekNum, theme)) + + if weekNum >= 3 { + sb.WriteString(". Championship bracket approaching — top 8 qualify") + } else { + sb.WriteString(". Early season — seeding phase") + } + + // Championship seed line + topBots := getTopBots(data, 3) + if len(topBots) > 0 { + sb.WriteString(". Current championship seeds: ") + names := make([]string, 0, len(topBots)) + for i, bot := range topBots { + names = append(names, fmt.Sprintf("#%d %s (ELO %d)", i+1, bot.Name, int(bot.Rating))) + } + sb.WriteString(strings.Join(names, ", ")) + } + + return sb.String() +} + func formatMapOfTheWeek(m *mapOfTheWeek) string { if m == nil { return "Not enough map data this week."