Fix catalog/base/skills/openrouter search returning zero results#5388
Fix catalog/base/skills/openrouter search returning zero results#5388richardhjtan wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ab5c18f742
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
Fixes a JSON:API identity mismatch in search-entry results for realms that store pristine_doc.id in registered-alias form (e.g. @cardstack/catalog/...). The projection engine now ensures the included item resource uses the same resolved URL (cardUrl) as the search-entry id and the item relationship, allowing consumers that hydrate via strict (type, id) matching on included to find the item.
Changes:
- Set included
item.idanditem.links.selfto the resolvedcardUrlinstead of the storedpristine_doc.id. - Add a regression test that simulates an alias-form stored
pristine_doc.idand asserts the included item is still keyed by the resolved id.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| packages/runtime-common/realm-index-query-engine.ts | Forces included item resources to use cardUrl for id/links.self so JSON:API relationship hydration matches. |
| packages/realm-server/tests/search-entries-engine-test.ts | Adds coverage for the alias-stored-pristine_doc.id case to prevent regressions in search-entry item hydration. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Host Test Results 1 files 1 suites 2h 16m 22s ⏱️ Results for commit 95f05d3. Realm Server Test Results 1 files ± 0 1 suites ±0 11m 37s ⏱️ + 2m 31s Results for commit 66d2718. ± Comparison against earlier commit 95f05d3. |
The `item` resource in a search-entry response inherited its id directly from the stored pristine_doc, which realms with a registered alias prefix (catalog, base, skills, openrouter) store in unresolved alias form (e.g. @cardstack/catalog/...) for storage portability. The item's search-entry relationship, however, always points at the row's resolved absolute URL. The mismatched ids meant a strict JSON:API (type, id) lookup on the consuming side never found the included item, so searches against these realms silently returned zero results. Set the item's id/links.self to the same resolved URL the relationship already uses.
Overriding the search-entry item's id to the resolved cardUrl (previous commit) left loadLinks comparing that against alias-form ids elsewhere in the same pass: a fetched linked resource's own id, and relationshipIdStr, both stay in registered-alias form for storage portability. A root that links to itself, or to another card already present as a search result, was no longer recognized as visited/omitted under its alias spelling, risking a duplicate included entry under a mismatched id. Index both the resolved and alias spellings of each omitted/visited root via VirtualNetwork.equivalentURLForms so either form matches.
equivalentURLForms only enumerates spellings of an already-resolved URL, so a root id arriving in registered-alias form (e.g. a root whose id retains pristine_doc's stored alias spelling) never got its resolved-URL form added to omitSet/visited. Resolve via toURLHref first — a no-op when the id is already a resolved URL or belongs to a non-aliased realm — so the normalization holds regardless of which form a caller's root ids arrive in.
95f05d3 to
66d2718
Compare
Origin
Found while debugging the Virtual Piano App in the public catalog: its in-card song search (a
context.getCardsquery for MusicSheet cards in the card's own realm) showed "No Music Sheets found" when the app ran from the catalog realm, yet the identical search worked after remixing the app into a personal realm. The backend returned all five MusicSheet results correctly — the host silently dropped every one of them at hydration because of the id mismatch below. Personal realms were immune only by coincidence: they have no registered alias prefix, so the stored id and the resolved URL happen to be the same string. (Same investigation also surfaced a realm-indexing failure mode, fixed separately in #5389.)Summary
itemresource inherited its id straight from the storedpristine_doc, which realms with a registered alias prefix (catalog, base, skills, openrouter) store in unresolved alias form (e.g.@cardstack/catalog/...) for storage portability across environments.(type, id)lookup onincluded(e.g. the host's search hydration) silently found nothing, so any search requestingfields[entry]=itemagainst these realms returned zero hydrated results despite the backend having correct, fully-indexed data.boxel_indexand found the same id mismatch present across a majority of instance rows in the catalog/base/skills/openrouter realms there.id/links.selfto the same resolved URL its relationship already uses, instead of trusting whatever form happened to be in storage.loadLinks'svisited/omitSetnow index every equivalent spelling of each root id (resolved first viatoURLHref, then expanded viaequivalentURLForms) so the resolved-form item id and alias-form linked-resource ids can't be treated as different cards.Test plan
pristine_doc.idand asserts the served item's id still matches its relationshipsearch-entries-engine-test.tsmodule run: 22/22 pass on the rebased branchtsc --noEmitclean🤖 Generated with Claude Code