Skip to content

Pair intro_engaged with game_started so the intro drop-off is measurable - #113

Merged
wachen merged 8 commits into
mainfrom
fix/intro-engaged-counting
Aug 18, 2026
Merged

Pair intro_engaged with game_started so the intro drop-off is measurable#113
wachen merged 8 commits into
mainfrom
fix/intro-engaged-counting

Conversation

@wachen

@wachen wachen commented Aug 15, 2026

Copy link
Copy Markdown
Owner

Why

This morning's GTCC newsletter traffic showed 39 game_started → 25 intro_engaged → 22 mode_chosen, reading as "36% of players picked a mode and never typed a character." That number is inflated by a counting artifact, not a real bounce rate.

introEngagedSent is a module-level flag scoped once per page load (deliberate — Intro unmounts on Back and on entering the board, so a per-mount ref double-counted engagement). But game_started fires on every ModePicker pick, and Intro's Back button returns to the picker.

So: pick board → type → Back → pick form = 2 game_started, 1 intro_engaged. Every mode switch manufactures a phantom bounce, and the two events never shared a denominator.

What

One line: the mode picker clears the flag alongside every game_started.

Resetting at onPick rather than at the three setPhase('pick-mode') call sites binds the reset to the exact statement that fires game_started, so they can't drift apart — a future fourth route back to the picker won't silently reintroduce the skew.

The original per-mount double-count this flag guarded against is unaffected.

Scope

Telemetry only. No gameplay, UI, or copy change. The greens contract, storage version, and /api/complete payload are untouched.

Notably this does not change the useful half of the signal: of the 25 who engaged, 22 cleared the five-field gate (88%), with only 2 intro_blocked all morning. The gate isn't rejecting people who try — that finding stands either way. This fixes the part that was unmeasurable.

Related but deliberately not included: roadmap U5 identity-at-end stays shelved per Wes.

Verification

  • bun run scripts/build.js + git status --porcelain -- dist → clean (gate passes)
  • bun test → 132 pass, 0 fail
  • Compiled dist/green-radius.js contains all four introEngagedSent sites (declaration, guard, set-true, new reset)
  • APP_VERSIONv113, CHANGELOG entry added

🤖 Generated with Claude Code

https://claude.ai/code/session_01B94AdnTqEvYxFjRLXQUMBb

intro_engaged was scoped once per page load (module-level flag) while
game_started fires on every ModePicker pick. A player who tried the board,
stepped back and picked the form logged two game_started against one
intro_engaged and read as a phantom bounce, inflating the measured
game_started -> intro_engaged drop.

The mode picker now clears the flag alongside every game_started, so both
events count the same population. Keeps the per-page-load flag's original
purpose (Intro unmounts on Back and on entering the board, so a per-mount
ref double-counted) while fixing the denominator.

Telemetry only, no gameplay change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B94AdnTqEvYxFjRLXQUMBb
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 15, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
green-radius-game e5fd5be Commit Preview URL

Branch Preview URL
Aug 18 2026, 05:48 AM

wachen and others added 7 commits August 15, 2026 16:45
Admins were getting bounced to the one-time-PIN email every 24h because
both Access session timers sit at their 24h default. Record the two knobs,
the ordering constraint between them (global must be >= application, or
raising the app value alone changes nothing), our chosen values, and the
policy-re-evaluation caveat that comes with a longer token.

Dashboard config only. verifyAccessJwt honours whatever exp Cloudflare
stamps, so there is nothing to change in the Worker.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B94AdnTqEvYxFjRLXQUMBb
Follow-up to 69bfbfc. Both timers now documented at 730h. Adds the policy
session duration warning (it outranks the application duration, so a stale
value there cancels both settings while looking like a save failure) and a
curl recipe to read the live values without clicking through the dashboard.

Note the live application session is still 336h: the API token available
here has Access read but not write, so the dashboard change is manual.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B94AdnTqEvYxFjRLXQUMBb
The signage deadline lands tonight, so the announcement strip switches to the
next ask: "Sign up to be included on the post-Burn online map!" Amber becomes
light purple to read as a new message rather than a lingering one.

Its auto-hide moves with it. SIGNUP_DEADLINE_END_MS was end of Aug 17 PDT, so
left alone the new copy would have gone dark a few hours after deploy; it now
runs to end of Sep 7 PDT (through the event) and still self-removes with no
removal deploy. Verified rendered at 390px against a local server: computed
background rgb(239,228,251), text rgb(91,46,144), no page errors.

APP_VERSION already reads v113 and this is still PR #113, so it is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B94AdnTqEvYxFjRLXQUMBb
Ponytail pass on #113, no behaviour change:

- green-radius.jsx: the intro_engaged comment narrated three iterations of a
  one-line fix. Kept the invariant (game_started fires per pick, so
  intro_engaged must reset per pick), dropped the history.
- docs/admin-setup.md: cut the curl+jq recipe — it duplicated the two dashboard
  paths the table right above it already names, and needed an API token nobody
  keeps around. Also folded the "ours are equal" paragraph into the rule that
  actually matters, that global must be >= application.
- src/home.jsx: SIGNUP_DEADLINE_END_MS/DeadlineBanner/data-deadline-banner
  guarded copy with no deadline left in it, so they read as SIGNUP_BANNER_END_MS
  /SignupBanner/data-signup-banner. Nothing outside this file referenced them.

Banner re-verified rendering at 390px after the rename: background
rgb(239,228,251), text rgb(91,46,144), no page errors. 132 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B94AdnTqEvYxFjRLXQUMBb
…isite

Review of #113 caught a factual error I introduced two commits ago. The doc
claimed the global session duration "must be >= the application duration or
raising only the app value changes nothing". Cloudflare's wording is "usually
set to equal or exceed" — a recommendation. The application token is what gates
each request, so a 1-month application session alone delivers month-long admin
logins; the global token only decides whether the renewal at that expiry is
silent or sends you back to the one-time-PIN email.

That mattered because global session is account-wide. Set to 730h it also let
the preview-URLs app (24h application session) renew unattended for a month, so
"the preview-URLs app stays at its own 24h" was misleading about its real
effective session. Global goes back to its 24h default; the doc now says why
the asymmetry is deliberate, and names all four timers rather than two.

Also fixes the missing blank line between the two #113 changelog bullets, which
rendered that pair as a tight item inside an otherwise loose list.

Cloudflare-side change (global back to 24h) is Wes's dashboard edit; the
application session stays at 730h and is already live.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B94AdnTqEvYxFjRLXQUMBb
The copy asks camps to sign up for the post-Burn online map, but the window
closed end of Sep 7 — the last day of the event, before the post-Burn ask is
topical at all. Window now ends Sep 30 PDT.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B94AdnTqEvYxFjRLXQUMBb
Reverts 1f3cf68. I read "after Sep 7" as "pick a date some weeks out" and took
end of Sep 30; Wes meant the Sep 8 boundary, which is where it already was.

Comment now states the instant the way he phrased it ("expires Sep 8 2026,
00:00 PDT") rather than "end of Sep 7 2026" — same millisecond, but the
end-of-day framing is what made the ask ambiguous in the first place.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B94AdnTqEvYxFjRLXQUMBb
@wachen
wachen merged commit a8f2e28 into main Aug 18, 2026
3 checks passed
@wachen
wachen deleted the fix/intro-engaged-counting branch August 18, 2026 06:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant