feat(admin): usage dashboard tab + user lifecycle column (Wave 0, #662) - #668
Merged
Conversation
Backend (routes/admin/analytics.ts): - GET /api/admin/analytics/usage — signups/day (Mongo, humans), messages/day + distinct human posters/day (PG), rolling DAU/WAU from lastActive, total users. days clamped 7-90. - GET /api/admin/analytics/lifecycle — per-user pod counts (Mongo aggregate) + message counts (PG GROUP BY), no N+1. Kept separate from /api/admin/users so moderation survives a PG outage. Frontend: - V2AdminAnalytics at /v2/admin/analytics (admin-gated): DAU/WAU/signup/ message/total cards, signups-per-day + messages-per-day SVG bars, and the #661 activation-funnel cohort table with rate summary. 7/30/90d range tabs. Zero-data renders stable empty charts. - V2AdminUsers: Joined column becomes Lifecycle ('joined 15d · 3 pods · 42 msgs'), merged from the lifecycle endpoint as a failure-isolated fetch; cross-links between the two admin pages. Tests: 8/8 backend (usage merge, bot-id passthrough to PG, lifecycle merge, 403s), 3 new frontend render tests (data, zero-data, API error). Closes #662 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MnRCAFgjrrGZxo9VRCmCm9
lastActive was only ever set at signup (schema default) — nothing wrote it afterward, so the funnel's returned-D1/D7 and the new DAU/WAU cards were structural zeros for every user. Wire it in the auth middleware: throttled to one fire-and-forget write per user per 15 minutes, with an in-memory map bounded by active-user count. A rejected write never fails the request and retries on the next one. Verified against live launch data: 34 signups ≥1 day old all showed D1=0 because of this, not because nobody returned. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MnRCAFgjrrGZxo9VRCmCm9
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MnRCAFgjrrGZxo9VRCmCm9
… 401 a valid request Surfaced by pgMessages.test.js: suites that mock models/User without updateOne made the telemetry call throw synchronously inside the auth try-block, turning valid requests into 401s. Telemetry never gets to break auth. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MnRCAFgjrrGZxo9VRCmCm9
lilyshen0722
added a commit
that referenced
this pull request
Jul 22, 2026
…r show 'never active' (#700) Every runtime-token request (agentRuntimeAuth) left User.lastActive at its creation-date default, so working agents rendered as never-active on profiles/rosters (Sam noticed codex-impl 'never live' while it was actively shipping PRs). Reuses the throttled fail-open touchLastActive from #668 (one write per user per 15min, never fails the request) at both agentUser assignment points (bot-user-token + legacy paths). Claude-Session: https://claude.ai/code/session_01MnRCAFgjrrGZxo9VRCmCm9 Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.
Second Wave 0 "See & Hear" ship (#662), building on the #661 funnel endpoint (#666).
Backend — two endpoints added to
routes/admin/analytics.tsGET /api/admin/analytics/usage?days=N— signups/day (Mongo, bot-excluded), messages/day + distinct human posters/day (PostgreSQL, bot ids passed through so agent posts don't inflate poster counts), rolling DAU/WAU fromlastActive, total human users.daysclamped 7–90, admin-gated, rate-limited.GET /api/admin/analytics/lifecycle— per-user{pods, messages}via one Mongo aggregate + one PG GROUP BY (no N+1). Deliberately not folded into/api/admin/users: moderation must keep working when PG is down, so the frontend merges this as a separate, failure-isolated fetch.Frontend
/v2/admin/analytics(admin-gated): DAU / WAU / signups / messages / total-users cards, signups-per-day and messages-per-day inline-SVG bar charts (no chart lib), and the activation-funnel cohort table with the rate summary line. 7/30/90d range tabs. Zero-data renders stable empty charts, not a crash./v2/admin/users: the Joined column becomes Lifecycle —joined 15d · 3 pods · 42 msgs(join date preserved in the cell tooltip). Degrades tojoined Ndif the lifecycle fetch fails. Cross-links between the two admin pages.Tests
Per CLAUDE.md the new layout gets a real-browser (Playwright MCP) check on live right after deploy — noting here so it's tracked; the page is admin-only so there's no anonymous exposure in the interim.
Closes #662
🤖 Generated with Claude Code
https://claude.ai/code/session_01MnRCAFgjrrGZxo9VRCmCm9