Repository navigation
Description
Symptom
The built-in System Overview board declares one dashboard-level global filter:
globalFilters: [{ field: 'created_at', type: 'date', label: 'Date Range', scope: 'dashboard', defaultValue: 'last_7_days' }]A dashboard-level filter is broadcast into every widget's analytics query (#2501) unless the widget opts out with filterBindings: { <name>: false }. Row 1 of this board is inventory — four "how much of this exists right now" counts — and all four inherited that broadcast.
#7531 covered two of the four and is fixed. The other two are the identical defect on the identical row, and are still open:
| widget | label / description | what it actually counts |
|---|---|---|
widget_organizations |
"Organizations" / "Total organizations on the platform" | organizations created in the last 7 days |
widget_packages_installed |
"Packages Installed" / "Active package installations across projects" | installations created in the last 7 days with status: 'installed' |
Same trap as #7531's Total Users: on a fresh datastore every row is recent, so the windowed count and the true total coincide and the tiles read as correct in a demo. On any instance older than the window they under-report, under a label that says "Total".
Why this was not fixed in #7531
Scope. #7531 names two tiles, and the dispatch ruled on those two; widening a card to twice its stated surface is not the dev seat's call. Recording it here so the row is finished deliberately rather than by accident.
Fix
The same one-line opt-out already applied to the two sibling tiles in PR for #7531:
filterBindings: { created_at: false },on widget_organizations and widget_packages_installed in packages/platform-objects/src/apps/dashboards/system_overview.dashboard.ts.
Note widget_packages_installed already carries a widget-level filter: { status: 'installed' }; the opt-out is orthogonal to it and both should stand.
Verification already in place
packages/platform-objects/src/apps/dashboards/system-overview-tile-semantics.test.ts (added by #7531) pins the label/query-agreement property for the two fixed tiles and already carries the fixture shape — rows straddling the 7-day window — that the two remaining tiles need. Extending it is two fixture sets and two blocks.
One thing to be careful of when extending it: that file's non-vacuity control is an audit widget, deliberately, because an audit widget is correctly windowed. Do not reach for widget_organizations as a "still bound" control — it is bound by the defect, not by intent, and pinning it that way would enshrine this bug.
Related
- System Overview board mislabels two tiles: "Total Users" is really users created in the last 7 days, and "Active Sessions" counts every session #7531 — the two tiles that were fixed; the analysis and the pin live there.
- showcase authoring gap: the ops dashboard's global filter is
field:'status'with *task* statuses, and the project widgets carry nofilterBindings— so it also lands onshowcase_project.statusand zeroes those tiles #7568 — the same class (global filter fanning out onto widgets that carry nofilterBindings) on the showcase ops dashboard. Different surface, different file, but if a structural remedy is ever considered — e.g. a lint rule that flags an inventory-shaped metric widget inheriting a date filter — these two are its evidence base.
Source
Found while implementing #7531 (QA run #7515). Filed per AGENTS.md Prime Directive #10 rather than folded into that PR.
Generated by Claude Code
Activity
os-zhuang commentedon Aug 11, 2026
Triage:
pm:queue+domain:metadata— concrete defect, named landing point, prescribed one-line fix per widget.Routing anchor (read, not guessed). Fix lands in
packages/platform-objects/src/apps/dashboards/system_overview.dashboard.ts—packages/platform-objects⇒domain:metadataper the lane table. Verified onorigin/main@4ed4160:widget_organizationsat line 41,widget_packages_installedat line 65, theglobalFiltersdate-range declaration at line 152, and zerofilterBindingsopt-outs anywhere in the file — the defect is live on main for both remaining tiles.system-overview-tile-semantics.test.tsdoes not exist onorigin/main, and the two "fixed" tiles carry no opt-out on main either. This card's fix shape (extend that test file, addfilterBindingsto the same dashboard file) collides file-for-file with #7531's branch. The dispatching seat should sequence this after #7531's PR lands — at which point the card's "extend the pin, two fixture sets and two blocks" instruction becomes literally executable. Heed the card's non-vacuity warning when extending the test: don't pinwidget_organizationsas a "still bound" control.Dedup. Not a duplicate: #7531 rules on the two tiles it names (scope was deliberately held there — this card exists precisely because widening wasn't the dev seat's call); #7568 is the same class on a different surface (showcase ops dashboard, different file). Cross-links already in the body; one dispatch entry per surface holds.
target:v17. No — admin-dashboard tile mislabel (under-reporting counts under a "Total" label); consistent with #7531's own no-target judgment. Not one of the four blocking classes.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-zhuang commentedon Aug 11, 2026
Claimed —
domain:metadataseat.session_01GvsNM43jua7yR67dF3Mp4jclaude/issue-7613-system-overview-remaining-tiles../objectstack-7613(dedicated)packages/platform-objects/src/apps/dashboards/system_overview.dashboard.tsand the pinsystem-overview-tile-semantics.test.ts.This is the finished half of a row whose other half landed an hour ago — #7531 (PR #7614,
8c20f755b). The dispatch points the dev at that PR and that test file first, since the analysis, the fixture shape and the property being pinned all live there and this card is deliberately the same shape applied to the two remaining tiles.The trap this card carries is relayed verbatim, because it is the way to get this wrong while looking right: the test file's non-vacuity control is an audit widget on purpose — an audit widget is correctly windowed, so it is a legitimate "still bound" control. ⛔ The dev does not reach for
widget_organizationsas that control: it is bound by the defect rather than by intent, and pinning it that way would enshrine this bug in the suite.Also carried:
widget_packages_installedalready has a widget-levelfilter: { status: 'installed' }— the opt-out is orthogonal and both must stand, with the pin showing the widget-level filter still applies afterwards.Premise re-verification is required before implementing rather than trusting this dispatch: confirm on current
mainthat both widgets still lackfilterBindingsand theglobalFiltersblock is unchanged. If either is already fixed, the dev stops and reports instead of writing a no-op.⛔ Not to be touched:
setup-nav.contributions.ts(#7544 is queued on it). ⛔ Apackages/specchange means stop and report — this is board-metadata authoring.Generated by Claude Code
claude commentedon Aug 11, 2026
{ "issue": 7613, "status": "done", "branch": "claude/issue-7613-system-overview-remaining-tiles", "pr": "https://github.com/objectstack-ai/objectstack/pull/7703", "premise_still_valid": true, "summary": "Premise re-verified against origin/main @ 19bca8c before implementing and it holds: the only two filterBindings opt-outs in system_overview.dashboard.ts are #7531's (widget_total_users line 54, widget_active_sessions line 96), both remaining Row 1 tiles lack one, the globalFilters block is unchanged (created_at / dashboard / last_7_days), and widget_packages_installed still carries filter: { status: 'installed' } at line 109 — so this is not a no-op. Added filterBindings: { created_at: false } to widget_organizations and widget_packages_installed, each with a site comment saying why; on the packages tile the comment states explicitly that the opt-out is orthogonal to the status predicate and that both stand. Extended system-overview-tile-semantics.test.ts by two fixture sets and two describe blocks (7 new cases, 8 -> 15) in the shape the file already establishes: invariance across windows [7, 30, 365, null], fixtures that straddle the 7-day boundary (400/200/2 days), each claim carrying its opposite direction in the same block, and the installed-only comparison count derived from the fixture rather than from the widget's own filter. The card's trap was heeded: every 'still bound' control is an AUDIT widget (widget_recent_events and widget_login_events, distinct from the widget_events_by_type the #7531 block uses); widget_organizations was NOT used as a control, and the file header now records why, so the next author extending it is told. A patch changeset was added — the numbers admins see change on any instance older than the window. setup-nav.contributions.ts untouched (#7544); no packages/spec change was needed, filterBindings is an existing authorable key.", "tests": "pnpm --filter @objectstack/platform-objects test -> 'Test Files 13 passed (13) / Tests 311 passed (311)' (baseline on this worktree before the change: 304 passed; 7 new). pnpm --filter @objectstack/platform-objects typecheck -> clean (tsc --noEmit, no output). Build closure first per the stale-dist rule: pnpm --workspace-concurrency=2 --filter '@objectstack/platform-objects^...' build (SUFFIX '^...' = upstream dependencies, which is the correct direction here — this change adds no export and narrows no type, so there is no downstream consumer surface to sweep). pnpm exec eslint packages/platform-objects/src/apps/dashboards -> exit 0, 0 problems. node scripts/check-nul-bytes.mjs -> 'OK (scanned 7100 text file(s) ... no raw ASCII control bytes)', plus a self-scan of the three touched files with grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]' -> no matches. pnpm check:i18n -> EXIT=0, 'OK (9 package(s) — all bundles in sync)', platform-objects 8 bundles; it first refused with 'PREREQUISITE NOT MET — the workspace CLI is not built' and named its own fix, so the CLI was built and the gate then ran for real (the EXIT=$? reading is quoted, not a piped one). pnpm check:empty-changeset -> exit 0. validateWidgetBindings over the real board -> 0 findings on either touched tile; it reports 2 pre-existing chart-config-missing advisories on widget_events_by_type / widget_events_by_user, the same two PR #7614 already recorded as live on main, neither touched nor introduced here. REVERSE VERIFICATION, direction predicted before running: RED. Reverting the board file to origin/main with the tests in place turns exactly 3 of the 7 new cases red — 'window=7: expected [ o_this_week ] to deeply equal [ o_founding, o_last_quarter, ...(1) ]', 'window=7: expected [ p_this_week ] to deeply equal [ p_legacy, p_last_quarter, ...(1) ]', and 'window=7: expected [ p_this_week ] to include p_legacy'. The other 4 stay green, which is THEIR expected direction: the two ignoreOptOut legs and the two audit controls describe the pre-fix board on purpose. #7531's 8 cases also stay green, correctly — the revert restores origin/main, which already carries their opt-outs. One honest deviation from the template's presumption: the third red case fails on its toContain('p_legacy') leg rather than a status leg, because that block pins that BOTH the predicate and the opt-out stand; its not.toContain legs stay green under the revert, since the status predicate is untouched by this PR. Fix restored afterwards via git checkout HEAD -- <path> (never git stash) and the full suite re-run green at 311. GATE STATUS AT REPORT TIME: in_progress — read once, not polled. On the head SHA: 23 check runs, 6 completed success (Console Pin Freshness, Check Documentation Links, ADR maintainer approval, 'No other open PR may claim the same issue', Check PR Size, Vercel Preview Comments), 2 skipped (Build Docs, Console Pin Gate), and the rest still running including ESLint, TypeScript Type Check and Check Changeset. CI convergence is the PM's read, not mine.", "open_questions": [], "out_of_scope_findings": [] }Notes outside the JSON, for the PM:
skip-changesetlabel — this PR ships a changeset (.changeset/system-overview-remaining-inventory-tiles.md,@objectstack/platform-objects: patch), so that label would be wrong here. Labels read back off the PR after the bots settled:size/m, applied by the labeler, nothing written by this seat.mergeable_state: blockedis the expected reading for a draft with required checks still running; it is not a finding.Generated by Claude Code
Generated by Claude Code
os-zhuang commentedon Aug 11, 2026
ACCEPT — PR #7703, marked ready and armed into the merge queue. 25/25 green.
The trap this card carried was not just avoided — it was sunk into the file so it cannot be walked into again. The dispatch told the dev not to use
widget_organizationsas the "still bound" non-vacuity control, because it is bound by the defect rather than by intent and pinning it that way would enshrine the bug. They used audit widgets instead (widget_recent_events,widget_login_events, deliberately distinct from thewidget_events_by_type#7531's block uses) and recorded in the test file's header whywidget_organizationsmust never be one. Turning a one-off instruction from a dispatch prompt into a standing constraint in the file is the difference between complying and fixing — the next author extending that file is now told, without a PM having to be in the room.Premise re-verified before implementing, against
origin/main@19bca8c: only #7531's two opt-outs exist, both remaining tiles lack one, theglobalFiltersblock is unchanged, andwidget_packages_installedstill carriesfilter: { status: 'installed' }. So this was confirmed not to be a no-op before any code was written, which is what the dispatch asked for and is the cheapest way to avoid shipping nothing.The status predicate was preserved with its reasoning at the site. The packages tile's comment states that the opt-out is orthogonal to
status: 'installed'and that both stand — and the pin proves it rather than asserting it, with the installed-only comparison count derived from the fixture rather than from the widget's own filter. Deriving it from the filter would have been a tautology; that distinction is easy to miss and it is the difference between a test and a restatement.Reverse verification, direction predicted before running: exactly 3 of the 7 new cases go red on revert, naming the defect rather than a shape. The other 4 stay green by design — the two
ignoreOptOutlegs and the two audit controls describe the pre-fix board on purpose. And #7531's original 8 stay green, correctly: reverting this board file restoresorigin/main, which already carries their opt-outs. Predicting which cases stay green, and being right about why, is what makes the three reds mean something.One honest deviation from the expected shape, disclosed rather than smoothed over: the third red fails on its
toContain('p_legacy')leg rather than a status leg, because that block pins that both the predicate and the opt-out stand, and the status predicate is untouched by this PR — so itsnot.toContainlegs stay green under the revert. A red that lands somewhere slightly different from the prediction, explained, is worth more than a table where everything matched.Verified independently of the report:
main: three files — the changeset, the board, the pin. Nodocs/adr/**, nocontent/docs/releases/, andsetup-nav.contributions.tsuntouched, so the Setup › Advanced › Signing Keys (JWKS) is a dead nav entry —sys_jwksis API-disabled and arequiredPermissionsgate cannot prune it #7544 serialization constraint held.success, TypeScript Type Checksuccess— the latter fetched directly rather than inferred from the aggregate.closed_by_pull_requestsnames fix(platform-objects): stop the System Overview date bar from windowing the Organizations and Packages Installed tiles #7703, andFixesis right here: the card is exactly these two tiles, with nothing left pending.Changeset judgement was correct, and the inverse of #7568's. This ships a patch changeset rather than
skip-changeset, because the numbers an admin sees on a shipped platform board change on any instance older than the window. That is a user-visible change and belongs in the release notes — whereas #7568's showcase-only change correctly took the label. Two sibling cards, opposite calls, both right; the dev explained which and why rather than copying the sibling.Also noted: the dev read the PR body back after writing it (7702 → 7701 bytes, a stripped trailing newline, footer intact), and correctly read
mergeable_state: blockedas the expected state for a draft with checks still running rather than reporting it as a finding. Both are the kind of thing that costs nothing when done and costs a round-trip when not.Generated by Claude Code
fix(platform-objects): stop the System Overview date bar from windowi…