Skip to content

Add Cards/Files/Definitions metadata to workspace favorites - #5677

Open
cmgardella wants to merge 8 commits into
mainfrom
workspace-chooser-favorites-metadata
Open

Add Cards/Files/Definitions metadata to workspace favorites#5677
cmgardella wants to merge 8 commits into
mainfrom
workspace-chooser-favorites-metadata

Conversation

@cmgardella

Copy link
Copy Markdown
Contributor

Summary

  • Favorite workspace tiles now show real Cards/Files/Definitions counts pulled from the realm index, replacing the previous card-count/recent-activity stats and the collaborator avatar stack.
  • The "New Workspace" tile now renders first in Your Workspaces instead of last, and workspaces/catalogs sort newest-first.
  • Added getFileCount/getDefinitionCount queries to Realm alongside the existing getCardCount, and threaded the new RealmInfo fields through.
  • Added a functional data-menu-item-id attribute to the shared Menu component so per-item CSS no longer has to select on a test-only data-test-* attribute.

Test plan

  • eslint and ember-template-lint pass on all changed files
  • Manually verified in a local dev environment: favorite tiles render varied Cards/Files/Definitions counts reflecting real realm content, the New Workspace tile appears first, and the collaborator avatars are gone

…New Workspace tile

Favorite tiles now show real Cards/Files/Definitions counts pulled from the
realm index instead of card-count/recent-activity stats, and the collaborator
avatar stack is removed. The "New Workspace" tile now sits first in Your
Workspaces instead of last.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@cmgardella
cmgardella requested a review from lukemelia August 3, 2026 18:43
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Preview deployments

Host Test Results

    1 files  ±    0      1 suites  ±0   2h 53m 18s ⏱️ + 2h 47m 10s
3 828 tests +3 672  3 814 ✅ +3 658  14 💤 +14  0 ❌ ±0 
3 847 runs  +3 691  3 833 ✅ +3 677  14 💤 +14  0 ❌ ±0 

Results for commit 05e0056. ± Comparison against earlier commit fcf1e08.

Realm Server Test Results

    1 files  ±    0      1 suites  ±0   15m 8s ⏱️ + 1m 40s
2 041 tests ±    0  2 041 ✅ +    1  0 💤 ±0  0 ❌  - 1 
2 120 runs   - 2 120  2 120 ✅  - 2 119  0 💤 ±0  0 ❌  - 1 

Results for commit 05e0056. ± Comparison against earlier commit fcf1e08.

lukemelia and others added 6 commits August 3, 2026 19:18
Cards/Files/Definitions counts and the realm's created/updated timestamps
now come from `Realm#getDetailedRealmInfo`, used by both `/_info` and the
realm server's batch `/_federated-info` — the latter is what the host's
workspace chooser actually reads.

They are deliberately not part of `parseRealmInfo`/`getRealmInfo`, whose
result is embedded in every card response's `meta.realmInfo` and hashed into
the card+json ETag: values that move on an ordinary realm write would
invalidate every card's cached representation in the realm whenever one card
changed, and cost extra queries on every card request.

Counting fixes:

- Count per distinct url rather than per row. A card instance is indexed as
  both an `instance` row and a `file` row at the same url, so counting rows
  put every card into the file count too.
- Drop the `generation = current_generation` predicate. That column is a
  last-touched watermark that an incremental index only bumps on the rows it
  rewrote, so pinning it counted the files touched by the most recent index
  pass rather than the realm's contents. Deletions are tombstoned via
  `is_deleted`, matching how the query engine scopes a live search.
- Use adapter-portable SQL. `count(*)::int` is Postgres-only and threw on the
  sqlite adapter the host tests use.

Read realm_metadata and realm_registry independently instead of joining them.
Keying the metadata read off realm_registry dropped showAsCatalog/publishable
for any realm with a metadata row but no registry row.

Drop recentActivityCount and collaboratorUsernames: nothing renders them, and
the collaborator list exposed every matrix user with realm access to any
realm reader — a wider audience than the owner-gated `/_permissions` route.

Workspace chooser:

- Keep the initial keyboard selection on a workspace. The New Workspace tile
  now renders first, and the selected tile takes focus, so starting there made
  the first Enter create a workspace instead of opening one.
- Restore the `.is-selected` ring, so keyboard-selected tiles stay visible.
- Give the favorite star a tooltip, sharing one getter with its aria-label.
- Collapse the three repeated stat blocks into one `tileStats` loop.

Tests: rewrite the keyboard-navigation tests for the new tile order, and add
coverage for the metadata row, tile ordering, catalog sort, the menu footer,
the star tooltip, the date formatters, and the index counts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
/_info runs an aggregate over every index row in the realm, and the realm
server's /_catalog-realms fans out to one _info per catalog realm on top of
the host's own calls. Uncached, that took per-realm _info from a ~44ms median
to ~200ms (max 762ms) in the matrix suite.

Cached separately from #cachedRealmInfo rather than folded into it, because
that object is hashed into the card+json ETag and these values move on every
realm write. Dropped by the same paths that drop #cachedRealmInfo, so the
counts still refresh on every index swap.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
/_catalog-realms issues one _info per publicly-readable realm, drops any
realm whose response isn't a 200, and caches the resulting list for the life
of the server process. Putting per-request index aggregation behind that cold
fan-out risked the catalog list for no benefit: the workspace chooser reads
its tile metadata from /_federated-info, which still serves the detailed
variant.

/_info is now byte-identical to main again. The count assertions move to
Realm#getDetailedRealmInfo directly, and a new test pins the contract that
/_info omits the extras.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Only a favorited workspace tile renders the Cards / Files / Definitions row,
but the counts were riding along on /_federated-info, which the host loads for
every realm at boot. The counts are the expensive half — an aggregate over
every index row in a realm — while the realm timestamps beside them are a
single indexed row lookup, so the two are split:

- /_federated-info keeps the timestamps. Needed more widely than the counts:
  createdAt orders the catalog list, and both feed the per-tile options menu.
- /_federated-index-counts is new, behind the same multiRealmAuthorization
  contract, and returns counts only for the realms it is asked about.

The host requests counts for favoriteRealmIdentifiers alone, from a modifier on
the Favorites list so it runs after render and re-runs when the set changes.
RealmService.loadIndexCounts is fire-and-forget and skips realms already loaded
or in flight, so the dashboard never waits on it; counts land in a tracked map
keyed by realm URL, separate from the realm info because they arrive on their
own schedule.

The stats row is now rendered unconditionally on favorite tiles with its height
reserved, so the numbers fill existing space rather than growing the tile and
shifting its centered name.

Counts stay memoized per index generation on the realm and are dropped by the
same invalidation paths as the realm info, so a re-render costs nothing and a
write is still reflected after the swap.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
If the realm fixture build fails, the realms are never assigned and an
unguarded teardown throws on unsubscribe before closeServer runs — leaking the
bound port so every later test in the process reports EADDRINUSE instead of the
original failure.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The realm server dropped its memoized counts on index swap, but the host kept
its copy for the session — so a favorited tile showed numbers from whenever the
chooser first asked. The only existing re-index refresh is deliberately narrow
(RealmConfig-card invalidations, for renames), and widening that would clobber
client-managed publish state.

Counts have no such hazard, so any completed index now marks them stale.
Marked, not refetched: the displayed numbers stay on screen so a write doesn't
blank the stats row, and a realm nobody is looking at costs nothing. The
workspace chooser's loader takes a revision argument that changes on
invalidation, which is what re-triggers the fetch on its next render — keyed
only on which realms are favorited, it would never re-run when the answer for
those realms changed.

Also correct the endpoint test's expected card count: a realm's own RealmConfig
card at realm.json is an instance, so the seeded realm holds two cards.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates workspace favorites to display real realm-index-derived metadata (Cards/Files/Definitions counts plus lifecycle timestamps), adjusts workspace/catalog ordering in the chooser, and introduces a shared menu-item identifier hook for styling without relying on test-only attributes.

Changes:

  • Added realm lifecycle timestamps (createdAt, updatedAt) to the boot-time federated realm info payload, while keeping index-aggregate counts on a separate, lazy path.
  • Introduced a new realm-server endpoint /_federated-index-counts and host-side caching/invalidation to load tile counts only for favorited realms.
  • Updated workspace chooser UI/behavior (favorite tile layout + tooltips, menu footer timestamps, new workspace tile ordering, and navigation expectations) and added data-menu-item-id to menu items.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/runtime-common/realm.ts Adds lifecycle timestamps plumbing and index-count aggregation + memoized APIs (getDetailedRealmInfo, getIndexCounts).
packages/runtime-common/index.ts Exports RealmIndexCounts.
packages/runtime-common/helpers/const.ts Adds helpers for asserting/stripping federated-only realm-info extras in tests.
packages/realm-server/routes.ts Registers the new /_federated-index-counts route.
packages/realm-server/handlers/handle-realm-info.ts Switches federated info to use getDetailedRealmInfo() (timestamps included).
packages/realm-server/handlers/handle-realm-index-counts.ts Implements the federated index-counts endpoint backed by Realm#getIndexCounts().
packages/realm-server/tests/server-endpoints/index-counts-test.ts Adds endpoint-level coverage for counts, auth behavior, and staleness after reindex.
packages/realm-server/tests/server-endpoints/info-test.ts Updates federated-info tests to assert presence/parseability of timestamp extras.
packages/realm-server/tests/server-endpoints/user-and-catalog-test.ts Updates catalog-realms test expectations to match plain /_info payload shape.
packages/realm-server/tests/realm-endpoints/info-test.ts Adds realm-level assertions for counts bucketing and timestamps; guards that /_info stays lean.
packages/realm-server/tests/index.ts Registers the new server-endpoints test module.
packages/realm-server/tests/helpers/index.ts Adds shared assertion helpers for realm-info extras and index-counts payloads.
packages/host/app/services/realm.ts Adds tracked, lazy index-count loading/caching + invalidation on index events; threads timestamps into default realm info objects.
packages/host/app/services/realm-server.ts Adds fetchRealmIndexCounts() client method for the new endpoint.
packages/host/app/components/operator-mode/workspace-chooser/index.gts Adds createdAt-based sorting, lazy count-loading modifier, responsive favorite tile sizing, and updated selection/nav model.
packages/host/app/components/operator-mode/workspace-chooser/workspace.gts Renders enlarged favorite tiles with stats/tooltips, adds timestamps footer in menu, and updates styling/hover behavior.
packages/host/tests/acceptance/workspace-chooser-test.gts Expands acceptance coverage for tooltips, favorite-tile stats behavior, tile ordering, and keyboard navigation changes.
packages/host/tests/unit/workspace-timestamp-labels-test.ts Adds unit coverage for relative-time formatting helpers.
packages/host/app/components/operator-mode/submode-layout.gts Updates top-bar center layout and avatar border token usage.
packages/boxel-ui/addon/src/components/menu/index.gts Adds data-menu-item-id attribute to menu item content for stable per-item styling hooks.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/host/tests/acceptance/workspace-chooser-test.gts Outdated
…omment

The divider's hardcoded #e8e8e8 is exactly --boxel-200, so this swap is
byte-identical in output while going through the palette like the rest of the
chrome.

The favorite-tile metadata module's comment still described the counts as coming
from each realm's /_info, which stopped being true when they moved to
/_federated-index-counts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lukemelia
lukemelia requested review from a team and burieberry and removed request for lukemelia August 4, 2026 17:00
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.

3 participants