feat: add Export GIF link to finished game pages - #145
Draft
byte-the-bot wants to merge 1 commit into
Draft
Conversation
Links to exporter.battlesnake.com with engine_url={BASE_URL}/api so the
exporter pulls frames from the engine-compatible endpoint shipped in
PR #127. Rendered only for finished games, as the last action in the
theater row, for anonymous and authenticated viewers alike.
byte-the-bot
force-pushed
the
implement/BS-c23cac20c43944fe
branch
from
August 16, 2026 08:02
505c3be to
1e0d4ba
Compare
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.
DO NOT MERGE UNTIL
Production
BASE_URLis set on Cloud Run. It is currently unset, soAppConfig::from_env()falls back tohttp://localhost:3000and this link wouldrender as a dead
engine_url=http://localhost:3000/api.This is a pre-existing prod bug, not one introduced here. The board replay iframe
already on every prod game page renders the same dead origin today, as does the Share
input. Evidence (2026-08-15),
curl -s https://arena.battlesnake.com/games/25d659a4-75b3-4c7a-8712-77d8c69aee15:So the Export GIF link would be exactly as broken as the replay viewer sitting directly
above it — no worse. Merging this before
BASE_URLis fixed is a defensible call; thedraft status is a safety default, not a veto. Corey decides.
Fix (needs write access to the Cloud Run service — Byte's gcloud identity is
byte-readonly@battlesnake-production.iam.gserviceaccount.com, read-only):
Use
--update-env-vars, NOT--set-env-vars: the latter wipes every other variableon the service (DATABASE_URL, GCP_LOGGING, ENGINE_DATABASE_URL, the Eyes IDs, ...).
This creates a new revision, i.e. a rolling restart of production arena.
After it lands: reload a finished game page and confirm the source now shows
https://arena.battlesnake.comin both the boardengine=URL and the Export GIFhref, then mark this PR ready for review.
Tracked follow-up for the deploy job: BS-583fb9517dc14ef9 (make
ci.ymlpass--update-env-vars BASE_URL=...so this stops being untracked manual state).Exporter gate
Ran 2026-08-15 against game
00c0ed77-51f4-4663-81b4-6d6f2e0df1cd:status=200 content_type=image/gif bytes=233989 time=4.90s,x-cache-hit: miss(fresh render,not a CDN replay), magic bytes
GIF89a. (A first attempt against game3bcc20ac-86bc-4f5d-bca5-ac79036ca2f9came backx-cache-hit: hitwithage: 31883— inconclusive per protocol, retried with a different game.)
Whoever flips this PR out of draft must rerun the full gate — fresh game discovery
from a snake profile, header capture,
x-cache-hit: missrequired — as the last actionbefore merging, and paste the result as a PR comment. Reusing the game ID above is not
acceptable: within 7 days it is a guaranteed cache hit.
What this does
Adds an Export GIF link to the game page's action row (
GET /games/{id}), renderedonly when the game's status is
finished, linking tohttps://exporter.battlesnake.com/games/{id}/gif?engine_url={BASE_URL}/api. Theexporter fetches frame history from the engine-compatible
{BASE_URL}/api/games/{id}/framesendpoint shipped in #127, so the
/apisuffix in the URL is load-bearing. Plain outboundanchor (
target="_blank" rel="noopener", bare.btnlike its siblings) — no proxying,no request-time health checks, no JavaScript; exporter downtime is the exporter's error
page. Renders for anonymous and authenticated viewers alike (not nested under the
auth branch). og:image/meta tags are out of scope by spec.
Deliberate limitations (decisions, not misses)
finishedwith no local frames, so they get a link thatrenders nothing. Accepted: they have no
game_battlesnakesrows, so every user-facinglisting excludes them — reachable only by typing a UUID; the page already shows its
degenerate "0 snakes" state, and
Game/get_game_by_iddon't carryarchived_at.If arena ever surfaces archived games in a listing, gate the link on
archived_at IS NULLat that time.== GameStatus::Finished, not an "is the game over?" heuristic.bytes. Harmless — finished games are immutable.
Tests
export_gif_urlexact-string test (full URL incl./gif,?engine_url=,trailing
/api— a regression can't silently fall back to the exporter's default engine).getByRole+toHaveCount(1).waiting/running/failed,toHaveCount(0).failure is the known Lima-VM-only
game-list.spec.tsjob-timing flake (fails onmaintoo, passes on CI).