feat(api): status endpoint, fort scan limit status, availability enrichment - #393
Conversation
(cherry picked from commit 71da944)
…y enrichment Golbat-side prerequisites for ccev/diadem#174. Branch already carries ccev's #392 as a cherry-pick (attribution preserved). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mirrors the v3 pokemon scan limit_reached (#392) on the gym, pokestop, station and combined fort scan responses, computed from the pre-collection key count against the effective limit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Secret-gated but not fort_in_memory-gated: consumers (ccev/diadem#174) detect capabilities and read the server's scan caps from it instead of probing gated endpoints. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mega/primal raid bosses were indistinguishable from their base form in gym raid availability, and showcase entries lacked the ranking standard. Both fields already exist on the fort records; thread them through FortLookup, the availability keys and the API results so consumer pick lists (ccev/diadem#174) can render them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Pokestop model (and ApiPokestopResult) declared the column int16 — a unix timestamp wraps int16, so DB reads and API output truncated. Gym's equivalent field was already int64. Note: nothing in Golbat currently assigns this field on new pokestops (it round-trips existing DB values only); populating it on first insert is a separate follow-up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
FortLookup is held per resident fort, so the two int16 enrichment fields cost a measured 8 bytes each after alignment (184 -> 192). Both values are tiny enums: as int8 placed in RaidLevel's pad byte and the pad before ShowcaseExpiry, unsafe.Sizeof returns 184 again — the enrichment now adds zero bytes per fort. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Memory note for the enrichment: the two new FortLookup fields are deliberately |
The fort API's design is native JSON for blob columns, but the pass was incomplete: gyms were fully converted while stationed_pokemon, showcase_focus, showcase_rankings and both quest_conditions still arrived as escaped strings. Convert all five via jsonRaw, matching the gym rsvps precedent. Consumers are unmerged (ccev/diadem#175, WatWowMap/ReactMap#1228 — the latter already handles both forms), so now is the time to break the wire. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Golbat is finishing its fort-API native-JSON conversion (UnownHash/Golbat#393): stationed_pokemon, showcase_focus and showcase_rankings switch from serialized strings to real JSON. Accept both wire generations via blobToString so either Golbat works, and type the wire fields honestly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Added the native-JSON completion for the remaining fort blob columns ( |
Cherry-picked from #397 (Mygod:codex/showcase-focus-filter) onto the fort API branch so the showcase work lands with the availability enrichment it depends on. Conflicts resolved by keeping both sides' additions: - FortLookup gains ShowcaseBuddyMinLevel alongside ShowcaseRankingStandard; both int8s share the pad before ShowcaseExpiry, so the struct stays 184 B. - showcaseKey keys on ranking standard AND structured focus. - ApiPokestopShowcaseAvailable carries ranking_standard and showcase_focus. Also corrected the showcase_focus nullability doc: it is null for legacy rows and for rows whose stored focus fails to parse, not only the former. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
first_seen_timestamp was written but never selected: it was absent from pokestopSelectColumns, the only load path for both single-row cache-miss loads and preload. The column never reached PokestopData, so every pokestop API response reported first_seen_timestamp 0 while the database held the real UNIX_TIMESTAMP() value written at insert. This also corrects the record from ab62fc9, which widened the field to int64 and described current behaviour as "existing DB values round-trip; new stops insert 0". Neither held: values never round-tripped because they were never selected, and new stops get a real UNIX_TIMESTAMP(), not 0. The widening was needed but inert on its own. The stored value was never corrupted by the gap because pokestop's upsert omits first_seen_timestamp from ON DUPLICATE KEY UPDATE -- unlike showcase_focus, which the upsert does write back and which had the same missing-column gap. Adds a test asserting every db-tagged field of PokestopData and GymData appears in its select-column constant. It fails without this change and would have caught the showcase_focus gap too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ing standard Availability advertised both dimensions with no way to request them. Adding RaidPokemonEvolution split mega and base bosses into distinct raid options, and ShowcaseRankingStandard split showcases by standard, but isFortDnfMatch read neither -- so a client could see that mega and base Mewtwo raids were both nearby and had no filter that could tell them apart. Both read fields already resident in FortLookup, so there is no new plumbing and no memory cost. Each follows the liveness gate its neighbours use: raid_temp_evolution_id joins raid_level/raid_pokemon_id behind the raid-active check, contest_ranking_standard sits behind the showcase expiry check. 0 is a real selector in both, not a wildcard: it means base-form boss (or unhatched egg) and unknown ranking standard respectively, matching how the availability payloads document those values. Invasion availability has the same gap for confirmed/slotN_pokemon_id, but that predates this branch and needs per-incident matching, so it is left out. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two field docs contradicted the code. /api/status listed "station by-id endpoints" among what fort_in_memory gates. It does not gate them: get-station and query-stations call GetStationRecordReadOnly, which falls back to loadStationFromDatabase on a cache miss and hydrates battles from the database either way; the flag only decides whether the rtree is updated as a side effect. Residency is a cache optimisation there, not a requirement, so a consumer reading fort_in_memory:false would have skipped calls that work. Only the scan and available routes 503 without the index, because without the rtree there is nothing to search. showcase_focus and showcase_rankings claimed "null when no showcase". They are never cleared on expiry -- deliberately, matching how the raid fields retain their last value -- so they are null only until a pokestop's first observed showcase and hold the last contest indefinitely after. The quest fields keep their "null when no quest" wording, which is accurate because ExpireQuests actively clears them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Brings in #393 (fort scan status/limits, showcase focus filtering, native JSON for fort blob columns) and the pokestop first_seen_timestamp fixes. Four conflicts, all shallow: - fortRtree.go, api_fort.go: main added code adjacent to lines this branch had already rewritten (showcase focus parsing next to the lookup key; fortScanLimit helpers above internalGetForts). Both sides kept. - api_pokestop_test.go, api_station_test.go: golden snapshots differing in two independent ways. Took main's JSON shape (quest_conditions, quest_rewards and stationed_pokemon now emit as native JSON rather than escaped strings) with this branch's parseable fort ids. Two test files main added compile against the old string-keyed types and were converted: api_fort_showcase_test.go (fortTree/fortLookupCache instantiation, the search closure, and its generated ids) and fort_availability_hooks_test.go's new Buddy showcase case. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Golbat-side prerequisites for the Diadem fort API migration (ccev/diadem#174 — the consumer design this serves).
Includes #392 as a cherry-pick with attribution preserved (thanks @ccev) — closes #392.
Deliverables
GET /api/status— secret-gated but notfort_in_memory-gated: reports enabled optional features and the server's scan caps so consumers can detect capabilities (and clamp their request limits) without probing gated endpoints:{ "features": { "fort_in_memory": true }, "limits": { "max_pokemon_results": 3000, "max_fort_results": 4000 } }limit_reachedon the fort scan responses (gym/pokestop/station/combined) — straight mirror of feat: expose v3 pokemon scan limit status #392: computed from the pre-collection key count against the effective limit (fortScanLimit= request limit clamped bytuning.max_fort_results).Availability enrichment —
temp_evolution_idon gym raid availability (mega/primal bosses were indistinguishable from base forms) andranking_standardon showcase availability. Distinct evolutions now yield distinct entries; both fields thread throughFortLookup→ availability keys → readers.Fix: pokestop
first_seen_timestampwasint16on both the model andApiPokestopResult— a unix timestamp wraps int16, truncating DB reads and API output (gym's field was already int64). Note while fixing: nothing in Golbat currently assigns this field for pokestops (existing DB values round-trip; new stops insert 0) — populating it on first insert is a separate follow-up.Verification
go build -tags go_json ./...go test -tags go_json ./decoder/ .(new: fort limit helper tests,/api/statusroute tests incl. 401 + not-503-gated assertions, availability enrichment tests incl. mega-vs-base distinctness)go test -tags go_json -race -run 'Availability|Observe…' ./decoder/golangci-lint run— 0 issues🤖 Generated with Claude Code