maniator / maniator/verticopolis
[P2] Settle what session_end measures across the three clock origins (web, desktop returning, desktop first run)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 5
- Forks
- 1
- PR merge metrics
- No merged PRs in 30d
Description
Raised by Codex on PR #802 (src/telemetry.ts:88) and deferred there after verification. The mechanism is real; the one-line fix it suggested is not the right shape, for the reasons below.
What is true
startGameplaySession() is called at src/bootstrap.ts:153, before await prepareSaveStore() at :171 and before create() at :173. The clock has no first-activity gate: begin() sets resumedAt = Date.now() (src/analytics.ts:137-139) and end() accumulates from it (:274-277), so the pre-hydration window is inside the reported session_end seconds.
On desktop that window is a real IPC bridge (getPlatform().saveStore, bound only for wrapped modes) that a browser build does not have; web statically folds bootstrap.ts:171 out. BOOT_STORE_TIMEOUT_MS = 3000 (src/game/desktopSaveHydrate.ts:31).
What was overstated
"Each phase and each record read can wait up to three seconds" is true per call but implies a compounding the code prevents. A dead bridge exits early at desktopSaveStore.ts:103 and costs 3s flat, and a timed-out record read aborts hydration at desktopSaveHydrate.ts:208 rather than continuing through all six sources. Only the reconcile-write loop can accumulate, and only when local caches moved ahead of the store.
Realistic healthy cost is 5-50ms, below the metric's own Math.round(activeMs / 1000) granularity. Stalled-bridge cost is ~3s, which is material only against the short tail (measured from session_end over 90 days, web: p10 3s, p25 12s, p50 70s).
Not a regression
src/bootstrap.ts is not in PR #802's diff. The ordering is exactly what is on main; the consent gate only made desktop analytics reachable.
Why the suggested fix is wrong
Moving only the desktop clock past hydration would define desktop session_end as "time after the app was ready" while web keeps "time from boot": two meanings under one metric name. A uniformly slightly inflated metric is better than a per-platform one.
It would also erase the signal for the sessions that matter most here: a player who hits a 3s stall and quits at 4s would report ~1s instead of ~4s.
The actual question to settle
session_end already has THREE different clock origins, and the largest gap is deliberate:
- Web: boot, with nothing awaited before
create(). - Desktop, returning + consented: boot, plus 0-3s of hydration.
- Desktop, first run: not at boot at all.
startGameplaySessionearly-returns while consent ispending(src/analytics.ts:477); the session is armed byarmSessionOnGrantwhen the player dismisses the notice (src/ui/uiDesktopAnalytics.ts:71-72, called at:115and:173), after first paint and after however long they spend reading it. That offset is seconds to minutes.
Fixing (2) while leaving (3) is straining a gnat.
Preferred resolution
Either:
- (a) Record boot/hydration duration as its own event property so the pre-play window is measurable and subtractable on both editions, keeping one clock origin; or
- (b) Make an explicit, documented decision about what
session_endmeans across the three cases above, and align them.
Do this when desktop sessions actually start arriving, since (3) dominates and cannot be evaluated without real first-run data.
If a stopgap is ever wanted, the minimal version is moving bootstrap.ts:153 to between :171 and :173 (after hydration, still before app construction so a create() throw leaves the session armed). Do not take it without settling the comparability question first.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Trace the three session-start paths in src/bootstrap.ts, src/analytics.ts, and src/ui/uiDesktopAnalytics.ts, including hydration in src/game/desktopSaveHydrate.ts and the save-store bridge. First compare the existing clock origins with the 90-day session_end data. Done means an explicit, documented cross-platform definition is chosen and either the metric is aligned or boot/hydration duration is recorded separately.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- analytics
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100