fix(platform-objects): make two System Overview tiles count what their labels say - #7614
Merged
Merged
Conversation
…r labels say (#7531) "Total Users" reported users created in the last 7 days: the board's `created_at` global filter is broadcast into every widget's analytics query (#2501) and `sys_user.created_at` exists, so the broadcast landed on a tile labelled "Total". "Active Sessions" counted every `sys_session` row, because the dataset is a bare count and the widget carried no predicate at all. Both are label/query disagreements on a shipped platform board, and both numbers were genuinely live — the query was answering a different question from the label. - `widget_total_users` opts out of the date bar with `filterBindings: { created_at: false }`. - `widget_active_sessions` filters `{ revoked_at: null, expires_at: { $gt: '{now}' } }` — the active predicate `sys_session` can actually express (ADR-0069 D4) — and opts out of the date bar too, because "currently active" is a statement about now rather than about a window. The date bar still reaches all six `sys_audit_log` widgets, which is what it was added for. No labels changed, so no translation key moves. The pin asserts label/query agreement rather than a snapshot: a total that is invariant across four windows, a sessions count equal to an independently computed active-only count, and an opposite direction for each so neither can hold vacuously. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018XWfHvFJPt37LcT7SoF2dJ
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 2 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
August 11, 2026 09:07
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.
Fixes #7531
Two tiles on the shipped System Overview board reported a different quantity from the one on the card. Both premises the dispatch hung its rulings on were checked against
origin/mainbefore implementing, and both hold — so both rulings are implemented as ruled, and neither tile was relabelled.Tile 1 — "Total Users" was a 7-day count
The board declares one dashboard-level filter,
created_atdefaulting tolast_7_days, and a dashboard-level filter is broadcast into every widget's analytics query (#2501).sys_user.created_atexists, so the broadcast landed on it and the tile reported "users created in the last 7 days" under a label that says "Total".Premise checked — is the date bar reaching this tile fan-out, or intent? Fan-out. The board is mixed, and the source says so in three places: the header comment splits it into "Platform KPIs" (row 1) and audit rows (2-4); every row-1 tile is labelled as a stock, not a flow ("Total Users", "Total organizations on the platform", "Number of currently active user sessions"); and the row-3 comment introduces
globalFiltersspecifically as "the supported way to scope these widgets", meaning thesys_audit_logdistribution charts. So this is not a 7-day activity board whose labels are wrong — it is an inventory row that inherited an activity filter.Fix:
filterBindings: { created_at: false }onwidget_total_users. That key is live and does what it says — objectuidashboard-filters.tsresolveBoundFieldhonoursfalseas an opt-out (packages/spec/liveness/dashboard.json, verified 2026-08-03).Tile 2 — "Active Sessions" counted every session
sys_session_metricsis a bare count oversys_sessionand the widget carried no predicate, so a signed-out or long-expired session was still reported as active.Premise checked — can
sys_sessionexpress "active"? Yes, exactly. ADR-0069 D4 gives itrevoked_at(set when a session is revoked by idle / absolute-max / concurrent-cap / admin) alongside the requiredexpires_at. A session is live while it has not been revoked and has not yet expired:{now}is a declared date macro resolved per request byresolveFilterTokens, whichservice-analytics'dataset-executorapplies to a widget'sfilter(itsruntimeFilter) — so this is a live predicate, not the unsubstitutedNOW() - INTERVALshape the row-3 comment warns about. Verified end to end before writing it: the token resolves to a full ISO timestamp and the predicate keeps exactly the unrevoked, unexpired rows.This tile also opts out of the date bar. That is not scope creep, it is what the ruling means: "currently active" is a statement about now rather than about a window, so an old session that is still live must still count. Left in, the tile would have reported "sessions created in the last 7 days that are active" — neither the old number nor the labelled one.
The date bar still does its job
All six
sys_audit_logwidgets (rows 2-4) still inherit it, and the test asserts that explicitly. No labels changed, so no translation key moves —pnpm check:i18nis green and reports all 8 platform-objects bundles in sync.The pin asserts agreement, not a snapshot
system-overview-tile-semantics.test.ts, 8 cases. A test that freezes today's numbers goes green again the day the label drifts back, so what is pinned is the property: the question a label asks and the question the effective query answers are the same question. Every claim carries its opposite direction in the same block, because each has a way to pass vacuously:The active-only count the tile is compared against is derived from the fixture directly, never from the widget's own filter — deriving it from the filter would make the comparison a tautology. Fixtures deliberately straddle the 7-day window, because a fixture where every row is recent makes every assertion in the file pass for free, which is the exact trap the card describes.
Only the composition of the dashboard filter into the widget query is modelled locally, because that step lives in objectui and no code in this repo performs it; the test states the precedence the
filterBindingscontract declares, the same rule@objectstack/lint'seffectiveFilterFieldmirrors. Everything downstream is the real machinery —resolveFilterTokensandmatchesFilterCondition, added as devDependencies — so a{now}that stopped resolving, or a$gtthat stopped meaning$gt, fails here.Reverse verification, direction predicted before running: red. Reverting the board file with the test in place turns 4 of the 8 cases red, naming the defect rather than a shape:
The four that stay green are the ones that describe the pre-fix board on purpose (the
ignoreOptOutlegs and the audit control), which is the expected direction for them.Verification
pnpm --filter @objectstack/platform-objects testpnpm --filter @objectstack/platform-objects typecheckpnpm --filter '@objectstack/platform-objects^...' builddistrulepnpm exec eslint packages/platform-objects/src/apps/dashboardsnode scripts/check-nul-bytes.mjspnpm check:i18npnpm check:empty-changeset,pnpm check:adr-0087-registrationvalidateWidgetBindingsover the real boardpnpm check:i18n-coveragecould not run in this worktree —@objectstack/connector-mcphas no build output, so 1 of 12 configs failed to lint and the gate refuses to judge a partial round. Environmental, unrelated to this diff; it needs a fullpnpm build, and CI does one.Out of scope, filed
created_atfan-out still windows the other two Row 1 inventory tiles, "Organizations" and "Packages Installed" #7613 — the samecreated_atfan-out still windows the other two row-1 inventory tiles, "Organizations" and "Packages Installed". Identical defect, identical one-line fix, deliberately not folded in: this card names two tiles and doubling its surface is not the implementing seat's call.validateWidgetBindingsalready reportschart-config-missingon bothwidget_events_by_typeandwidget_events_by_useronmain("the renderer cannot determine which measure to plot, so the series renders empty"). That is a pre-existing advisory, not something this PR changes, and it is not a diagnosis — but a reproduction attempt may want to rule it in or out before blaming the null-category drop.setup-nav.contributions.tswas not touched, per the #7544 serialization constraint.Generated by Claude Code