Skip to content

Query-backed linksToMany: skip live re-query inside a prerender#4927

Merged
habdelra merged 2 commits into
mainfrom
cs-11235-prerender-not-live-query-field
May 21, 2026
Merged

Query-backed linksToMany: skip live re-query inside a prerender#4927
habdelra merged 2 commits into
mainfrom
cs-11235-prerender-not-live-query-field

Conversation

@habdelra
Copy link
Copy Markdown
Contributor

Summary

  • Query-backed linksToMany fields no longer construct a live SearchResource inside a prerender; they resolve from the seed that the parent doc already serialized.
  • On a card with N query-backed linksToMany fields fanning out across M loaded child cards, the per-render _federated-search cascade collapses from O(N*M) to just the top-level type searches the template itself issues.

The mechanism

Today: query-field-support.ts creates a SearchResource with isLive: true for query-backed linksToMany fields. Correct for the live SPA — a user expects the list to reflect concurrent writes. Inside a prerender it triggers a _federated-search round-trip per field per loaded card to re-validate what the parent doc's relationships.{field}.data already states.

After: isLive: !Boolean(globalThis.__boxelRenderContext). In prerender the SearchResource resolves from the seed and exits before issuing any search; in the SPA, behavior is unchanged.

To make the seed-only path authoritative regardless of small signature drift between the parent doc's links.search and the recomputed query, SearchResource.modify was updated: when isLive is false and a seed is provided, after applySeed it returns early and skips the query/realm equality check below.

Why not live in prerender

  • The indexer just wrote the rows being rendered; the data isn't moving.
  • The prerender output lives in the index and is regenerated by the dep graph, not by client subscription.
  • If the live re-query returns a different cardinality than the parent doc's serialized relationships, the rendered HTML iterates a different set than the parent doc describes — an internal-inconsistency window that isLive: true introduces in prerender mode specifically, and isLive: false closes.

Scope of other isLive: false callers

Audited every other non-live caller of getSearchResource / getSearch. None pass a seed, so the new "non-live + seed = skip search" short-circuit only affects the query-field path.

Test plan

  • Existing search-resource integration tests pass
  • New test: no _federated-search fetch fires in prerender mode (__boxelRenderContext = true) when a seed is present
  • New test: live SPA path with a matching seed still resolves to the correct set (live behavior preserved)
  • New test: non-live + no-seed callers still fetch (other consumers unaffected)

Related: CS-11235

The query-field SearchResource was constructed with isLive: true
unconditionally. Inside a prerender the parent doc's
relationships.{field}.data is already the authoritative cardinality
for the field — the indexer just wrote it. Live re-query there
turns into a _federated-search round-trip per query-backed
linksToMany field per loaded card, and additionally introduces an
internal-inconsistency window (live result vs serialized
relationships) that is unique to the prerender code path.

This change:

  * query-field-support: derives isLive from
    !globalThis.__boxelRenderContext so prerender gets a non-live
    resource and the SPA path is unchanged.

  * SearchResource: when isLive is false and a seed is provided,
    short-circuits after applying the seed and skips the search
    perform entirely. The seed cards are the authoritative result;
    bypassing the query/realm equality check below means a
    signature drift between the parent doc's links.search and the
    recomputed query cannot sneak a fetch back in.

  * tests: adds a non-live + seed module to the search resource
    integration tests that asserts (a) no _federated-search fires
    when isLive=false with a seed under a render context, (b)
    live + seed still resolves to the correct set, (c) non-live
    + no-seed still fetches.

Related: CS-11235
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 50e65b0fc8

ℹ️ 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".

Comment thread packages/host/app/resources/search.ts
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 optimizes prerender performance for query-backed linksToMany fields by avoiding redundant live _federated-search round-trips when the relationship can be resolved from the parent document’s serialized seed data.

Changes:

  • In prerender contexts, query-field SearchResource instances are created as non-live so they can resolve from seed data without re-querying.
  • SearchResource.modify() short-circuits when isLive=false and a seed is present to avoid re-running the search/equality checks.
  • Adds integration tests to assert “non-live + seed” does not fire _federated-search, while ensuring other paths still behave as expected.

Reviewed changes

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

File Description
packages/host/tests/integration/resources/search-test.ts Adds integration coverage for non-live SearchResource behavior with/without seed and compares against live behavior.
packages/host/app/resources/search.ts Updates SearchResource.modify() to treat non-live seeded resolution as authoritative and skip subsequent search execution.
packages/base/query-field-support.ts Sets query-field SearchResource isLive based on prerender context to prevent prerender search cascades.

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

Comment thread packages/host/app/resources/search.ts Outdated
Comment thread packages/host/tests/integration/resources/search-test.ts
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 21, 2026

Preview deployments

Host Test Results

    1 files      1 suites   1h 49m 41s ⏱️
2 716 tests 2 701 ✅ 15 💤 0 ❌
2 735 runs  2 720 ✅ 15 💤 0 ❌

Results for commit aed6c4b.

Realm Server Test Results

    1 files  ±0      1 suites  ±0   11m 17s ⏱️ +39s
1 480 tests ±0  1 480 ✅ +2  0 💤 ±0  0 ❌  - 2 
1 571 runs  ±0  1 571 ✅ +2  0 💤 ±0  0 ❌  - 2 

Results for commit aed6c4b. ± Comparison against earlier commit 50e65b0.

Only short-circuit the perform() call when the seed is authoritative —
either seed.cards.length > 0, or seed.searchURL is set. An empty seed
with no searchURL is the explicit unresolved signal from
query-field-support's shouldTreatEmptySeedAsUnresolved branch, and
must still run the fallback fetch — otherwise relationship items go
missing in the rendered HTML.

Adds a regression test covering the empty-unresolved-seed shape in
prerender context.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@habdelra habdelra requested a review from a team May 21, 2026 20:51
@habdelra habdelra merged commit 18bc6c3 into main May 21, 2026
101 of 102 checks passed
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