maniator / maniator/verticopolis

[Feature]: [P2] session_fps field telemetry + Frame health section in the analytics report

Open
#538 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
TypeScript
Stars
5
Forks
1
PR merge metrics
No merged PRs in 30d

Description

> From the full repository audit (2026-07-21, recommendation N-1; report at _bmad-output/planning-artifacts/reviews/full-repository-audit-2026-07-21/), design shaped with the owner in session. Backlog row session-fps-telemetry.

Problem

Nothing measures real-player frame rate. The audit measured hard scaling on real saves (engine tick 8.6 ms/game-minute, 53 ms hour-boundary spikes at 25k units) but its absolute FPS numbers came from a GPU-less container; the render-perf spec says on-device data should gate the remaining tuning (drain budget, hour-boundary amortization, tick follow-ups in #539). Field data decides whether and where that effort goes.

Design

  • Capture in runFrame (src/game/frameLoop.ts receives dtMs): a fixed array of frame-time buckets (<8, 8-16, 16-33, 33-66, >66 ms), one integer increment per frame, no allocation. Pause counting while document.hidden, while paused/in menus, and for a warm-up window after load or import.
  • Emit once per session at the first background after gameplay (the lesson #531 learned: session_end re-fires per tab-hide and there is no session id to dedup by), via the existing GameplaySession machinery.
  • Payload shape depends on decision D-1 (audit §19, tracked in #542): under Vercel WA, named bands plus a combined fps_units: "30-45|5k-15k" property (25 values, under the 100-bucket cap) to get the fps-by-tower-size correlation; under PostHog, raw p50/p95 values with real percentiles server-side and no band tricks.
  • Report: a "Frame health" section in scripts/analytics-report.mjs in the shape of the merged Depth section (share of sessions per band, fps x tower-size matrix), or its PostHog equivalent.

Dependencies

  • The merged #531 session machinery (in main).
  • Payload/report shape finalized by D-1 (#542). Capture-side work is identical either way and can start first.

Acceptance criteria / required tests

  • Bucket counting is exercised by a unit test (hidden/paused/warm-up exclusions pinned); zero allocation on the frame path (no new objects in runFrame).
  • At most one session_fps emission per session, pinned like the depth-events once-per-session test.
  • Report section renders with real and empty data, with the same skip/truncation honesty as the Depth section.
  • Event property cardinality stays under the platform's group-by cap.

Analytics/tooling: /bmad-code-review.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with src/game/frameLoop.ts and the existing GameplaySession and depth-events once-per-session tests, then read scripts/analytics-report.mjs and decision D-1 in #542. Confirm the finalized payload shape before implementing. Done means frame buckets exclude hidden, paused, menu, and warm-up periods; emission occurs once per session; and the report handles real and empty data while preserving cardinality and allocation constraints.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, typescript
Domain
analytics
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.