Recognize Workspace as a default realm index card#5585
Conversation
Both the publish handler and the prerender fast-path only recognized CardsGrid as a realm's default index card. Generalize each to a list of recognized default index cards (CardsGrid + Workspace): - publish handler: opt a realm into includePrerenderedDefaultRealmIndex when its index.json adopts any recognized default index card. - render/html.ts: short-circuit the isolated render to the boilerplate when the index card's top type is any recognized default index card and the realm has not opted in. - the boilerplate placeholder is generalized (it serves both), dropping cards-grid-specific class names. Covers the publish-handler recognition (realm-server) and the useRealmIndexBoilerplate recognition (host realm-indexing) with tests mirroring the existing CardsGrid cases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Preview deploymentsHost Test Results 1 files ±0 1 suites ±0 3h 3m 8s ⏱️ + 3m 14s Results for commit 632a1b9. ± Comparison against earlier commit 6cd5b90. Realm Server Test Results 1 files ±0 1 suites ±0 11m 43s ⏱️ - 1m 13s Results for commit 632a1b9. ± Comparison against earlier commit 6cd5b90. |
habdelra
left a comment
There was a problem hiding this comment.
[Claude Code 🤖] Reviewed with a focus on completeness of the generalization — the risk in a "recognize one more card type" change is a missed twin recognizer or a downstream consumer still hard-coded to the old type, so I traced every place that decides "is this the realm's default index card" and every reference to the renamed CSS hooks.
Bottom line: this is a clean, correctly-scoped change with no blocking issues. Both recognizers were updated symmetrically, the rename is safe, and the tests pin the new behavior. Only two minor comment/doc drifts to tidy (both non-blocking).
What lands right
- Both recognizers updated, and they agree. The publish handler (
isDefaultRealmIndexAdoption, readingindex.json'sadoptsFrom) and the host render route (#isDefaultRealmIndexCard, comparingtypes[0]viainternalKeyFor) are the only two places that classify the default index card. Both were generalized the same way, and they resolve to the same answer for a directly-adoptingindex.json. Details in the inline note onhandle-publish-realm.ts. - The CSS-hook rename is safe.
boxel-cards-grid-shell/data-boxel-cards-grid-index→boxel-realm-index-shell/data-boxel-realm-index. I grepped the tree: the old names have zero remaining references, and the new names appear only inrealm-index-boilerplate.ts. Nothing selects on these (they exist for hydration-replaceable markup), so the rename is inert beyond readability. - No third recognizer downstream. The published-realm SSR injection path keys off the
includePrerenderedDefaultRealmIndexflag (read from RealmConfig on disk intoRealmInfo), not the card type — so writing the flag from the publish handler is the whole story; nothing else needed generalizing. - Tests mirror the CardsGrid cases faithfully. The host test asserts a not-opted-in Workspace realm gets
REALM_INDEX_BOILERPLATE_HTML, and the realm-server test asserts publishing a Workspace-indexed realm writes the opt-in flag — each a line-for-line analogue of the existing CardsGrid test. Both are green on the head commit in CI.
On the one red check (Host Memory Baseline) — not caused by this diff. It fails on "modules not in baseline," and the modules it names are all the Workspace card's own test suites, which come from the base branch this PR stacks on, not from this PR (which adds no new test module and no new retained state). It should re-baseline once the stack retargets to main. Worth a glance to confirm, but not a regression here.
Recommendations (all non-blocking)
html.ts— update the staleModel.useRealmIndexBoilerplatedocstring ("the type chain is base CardsGrid") to match the generalized sibling comments. See the file comment.handle-publish-realm.ts— optionally generalize the opening of the "The CardsGrid CardDef can be referenced two equivalent ways…" comment so the shared rationale reads as card-agnostic. See the inline note.
Adjacent, out of scope — RealmInfo.includePrerenderedDefaultRealmIndex in packages/runtime-common/realm.ts is documented as opt-in "for the realm's default CardsGrid index card." This PR widens that field's meaning to Workspace-indexed realms too, so the field doc is now narrower than the behavior. That file isn't in this diff; a one-word tweak there (or in a follow-up) would keep the doc honest.
Generated by Claude Code
| const DEFAULT_REALM_INDEX_ADOPTIONS: { name: string; modules: Set<string> }[] = | ||
| [ | ||
| { | ||
| name: 'CardsGrid', | ||
| modules: new Set([ | ||
| 'https://cardstack.com/base/cards-grid', | ||
| '@cardstack/base/cards-grid', | ||
| ]), | ||
| }, | ||
| { | ||
| name: 'Workspace', | ||
| modules: new Set([ | ||
| 'https://cardstack.com/base/workspace', | ||
| '@cardstack/base/workspace', | ||
| ]), | ||
| }, | ||
| ]; | ||
|
|
||
| function isDefaultRealmIndexAdoption(adoptsFrom: { | ||
| module: string; | ||
| name: string; | ||
| }): boolean { | ||
| return DEFAULT_REALM_INDEX_ADOPTIONS.some( | ||
| (adoption) => | ||
| adoption.modules.has(adoptsFrom.module) && | ||
| adoption.name === adoptsFrom.name, | ||
| ); | ||
| } |
There was a problem hiding this comment.
[Claude Code 🤖] Confirmation — the recognizer is correct and matches its twin. Non-blocking.
isDefaultRealmIndexAdoption AND-checks module membership against name equality, so it won't misfire on a card named Workspace living in a different module, nor on a CardsGrid-named card in the workspace module. Both module spellings per card mirror the existing CardsGrid pair, and the base-realm URL for the new entry (https://cardstack.com/base/workspace) is the correct absolute form for packages/base/workspace.gts.
This is one of the two places that recognize a realm's default index card. I checked the other — #isDefaultRealmIndexCard in packages/host/app/routes/render/html.ts — and it was generalized the same way (comparing types[0] against DEFAULT_REALM_INDEX_REFS via internalKeyFor). The two agree in the common case that matters here: an index.json that directly adopts the base def produces an instance whose own type (types[0]) is that base def, so the disk-adoptsFrom read and the resolved-type read land on the same answer. I also confirmed there's no third recognizer downstream — the published-realm SSR path keys off the includePrerenderedDefaultRealmIndex flag this handler writes, not the card type — so generalizing these two is complete.
Minor, optional: the comment block just above (beginning "The CardsGrid CardDef can be referenced two equivalent ways…") still frames the two-module-forms rationale as CardsGrid-specific, even though it now applies to every entry in this list. Consider opening it with "A base CardDef can be referenced two equivalent ways…" so the note reads as general, since the generalized comment below points back to it with "see the note above."
Generated by Claude Code
There was a problem hiding this comment.
[Claude Code 🤖] Done in 632a1b9 — opened the comment with "A base CardDef can be referenced two equivalent ways…" so the shared two-module-forms rationale reads as card-agnostic, matching the "see the note above" pointer below it.
There was a problem hiding this comment.
[Claude Code 🤖] Stale comment left behind by the generalization. Non-blocking (doc drift).
The Model.useRealmIndexBoilerplate docstring near the top of this file still reads:
Set only when
format === 'isolated', the card is the realm's default index, the type chain is base CardsGrid, and the realm has not opted in…
That sentence describes the pre-PR behavior. The change generalized the check to CardsGrid or Workspace and updated the sibling comments — the method's own docstring on #isDefaultRealmIndexCard, the comment in packages/host/app/components/card-prerender.gts, and the boilerplate constant's header all now say "CardsGrid or Workspace" — but this interface docstring was missed. Suggest matching it, e.g. "the type chain begins with a recognized default index card (CardsGrid or Workspace)", so the two comments in this file don't contradict each other.
Generated by Claude Code
There was a problem hiding this comment.
[Claude Code 🤖] Fixed in 632a1b9 — the useRealmIndexBoilerplate docstring now reads "the type chain begins with a recognized default index card (CardsGrid or Workspace)", consistent with the sibling comments. Also generalized the RealmInfo.includePrerenderedDefaultRealmIndex field doc in packages/runtime-common/realm.ts (the adjacent out-of-scope item) in the same commit.
Three doc comments still described the default-index recognition as CardsGrid-specific after the recognizers were generalized to accept Workspace too. Align them with the generalized behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What
Both the publish handler and the prerender fast-path only recognized
CardsGridas a realm's default index card. This teaches them to recognizeWorkspacetoo, so Workspace-indexed realms get the same default-index handling.Changes
handle-publish-realm.ts— the CardsGrid-specificCARDS_GRID_MODULE_FORMS+ name check becomes a list of recognized default-index adoptions (CardsGrid + Workspace). Publishing a realm whoseindex.jsonadopts either now writesincludePrerenderedDefaultRealmIndex: trueso the published homepage gets real prerendered content.routes/render/html.ts—#isDefaultRealmCardsGridIndex→#isDefaultRealmIndexCard, comparing the index card's top type against a list of recognized default-index refs viainternalKeyFor. A realm indexed by CardsGrid or Workspace (and not opted in) short-circuits the isolated render to the boilerplate placeholder.realm-index-boilerplate.ts— the placeholder serves both index cards now, so its cards-grid-specific class/data attributes are generalized to realm-index-neutral names (no other consumers reference them).A realm with a bespoke (non-default) index card is still left alone in both paths.
Why this matters for the project
Making Workspace the actual default for new realms (
create-realm.ts) depends on this — otherwise a newly created Workspace-indexed realm would lose the prerendered-default-index fast-path on publish.Tests
publish-unpublish-realm-test.ts) — publishing a realm whose index adopts Workspace writesincludePrerenderedDefaultRealmIndex, mirroring the existing CardsGrid case.realm-indexing-test.gts) — a Workspace-indexed realm that has not opted in gets the boilerplate placeholder as its isolated HTML, mirroring the existing CardsGrid case.Stacking
Based on the Workspace port branch — the recognition is meaningless without the card, and the tests set up Workspace-indexed realms. Retargets to
mainonce the port merges. Draft until then.Test plan
realm-server
ember-tsc+ host glint clean; eslint + prettier clean. Host boilerplate test run locally against a full dev stack — 1 passed. The realm-server publish test is static-verified and mirrors an existing passing test; it runs in CI (the realm-server publish harness needs test-pg + its own prerender, not wired up in this local session).🤖 Generated with Claude Code