Skip to content

Reconcile eligible live searches against in-memory Store state#5327

Draft
lukemelia wants to merge 2 commits into
mainfrom
cs-11416-integrate-client-filtering-step-into-searchresource-reactive
Draft

Reconcile eligible live searches against in-memory Store state#5327
lukemelia wants to merge 2 commits into
mainfrom
cs-11416-integrate-client-filtering-step-into-searchresource-reactive

Conversation

@lukemelia

@lukemelia lukemelia commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

What

Wires the runtime-common instance matcher/comparator into SearchResource so an eligible live search derives its displayed result set from the server result reconciled against in-memory Store state — the list self-corrects the instant a card is created, edited, or deleted, before the realm reindexes. One-shot StoreService.search() and non-live searches are unaffected.

This builds the full-recompute version; an incremental / dirty-set optimization is out of scope and left for separate, measurement-gated work.

How

  • SearchResource.displayedInstances (new, backs the instances getter): for an eligible live search it
    • keeps every server-returned instance unless it now demonstrably fails the filter locally (an unresolvable predicate never removes a server result),
    • adds Store candidates that match the filter but are absent from the server result, scoped to the query's target realm(s) (unresolvable candidates are not added),
    • dedupes by id and re-sorts with the query's sort comparator.
  • Eligibility (isClientFilterEligible): live search + matcher loaded + a complete result set (instances.length === meta.page.total) + a client-evaluable filter. Anything else (non-live, paginated/incomplete, matches/full-text) is a server-only passthrough.
  • Reactivity: recomputes on Store mutation, not only when the server search re-runs — it reads the tracked identity map (covers create/delete) and a new StoreService.instanceMutationVersion signal (covers in-place edit/save).
  • StoreService exposes the candidate pool (allCardInstances / allFileMetaInstances) and a getMatchAPI() slice for the matcher.

Notable correctness detail

The Store keys each instance under both its local id and its remote id, so an un-deduped candidate pool returns the same card twice — which the merge would render as two rows. allCardInstances / allFileMetaInstances dedupe by instance identity, and the merge dedupes by id as a second guard. Regression test included.

Demo

experiments-realm/client-filter-playground.gts — two live searches (active / archived) over one Store, each sorted by priority. Create, archive/activate, re-prioritize, or delete a widget and both lists reconcile in place with no _federated-search round-trip (the server reindex catches up a beat later). The list renders whenever it has results, so a background live-refresh doesn't blank it.

Tests

tests/integration/resources/search-test.ts, client-side Store filtering step module:

  • locally added matching card appears with no server round-trip; a server card that no longer matches locally is removed; editing a candidate toggles membership reactively; merged set is ordered per the sort.
  • a surfaced candidate is not duplicated despite dual local/remote-id keying (no duplicate ids in the displayed set).
  • passthrough cases: one-shot store.search, non-live, incompletely-loaded (paginated), and non-client-evaluable (matches) all stay server-only.

Notes for reviewers

  • isLoading is search.isRunning, so it flips true on every background live-refresh. Consumers that render a loading branch ahead of their results will blank on each round-trip — the demo orders its template to render results when present to avoid this. A resource-level isRefreshing / initial-load-only distinction would give consumers flash-free behavior by default; left out here to avoid changing shared isLoading semantics.

🤖 Generated with Claude Code

lukemelia and others added 2 commits June 24, 2026 00:06
Wire the runtime-common instance matcher/comparator into SearchResource so an
eligible live search derives its displayed result set from the server result
reconciled against the Store, rather than the raw server result alone:

- Add Store candidates that match the filter but are absent from the server
  result, scoped to the query's target realm(s); remove server cards that no
  longer match locally. Unresolvable predicates never add a candidate and never
  remove a server card.
- Re-sort the merged set with the query's sort comparator.
- Recompute reactively on Store mutation (create/edit/save/delete) via the
  tracked identity map and a Store mutation-version signal — not only when the
  server search re-runs.

Eligibility is gated to live searches with a complete, client-evaluable result
set; one-shot StoreService.search() and non-live/paginated/matches searches stay
server-only passthroughs.

StoreService exposes the candidate pool (allCardInstances / allFileMetaInstances)
and a getMatchAPI() slice for the matcher. The candidate pool dedupes by instance
identity: the Store keys each card under both its local and remote id, so an
un-deduped pool would surface — and the merge would render — the same card twice.
The merge dedupes by id as a second guard.

CS-11416

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A live demo of the client-side Store filtering step: two live searches (active
and archived) share one Store, each sorted by priority. Creating, archiving,
re-prioritizing, or deleting a widget reconciles both lists in place before the
realm reindexes, with no server round-trip. The list renders whenever it has
results so a background live-refresh doesn't blank it.

CS-11416

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lukemelia lukemelia force-pushed the cs-11416-integrate-client-filtering-step-into-searchresource-reactive branch from eacca56 to 9c2642e Compare June 24, 2026 04:07
@lukemelia lukemelia changed the title Reconcile eligible live searches against in-memory Store state (CS-11416) Reconcile eligible live searches against in-memory Store state Jun 24, 2026
@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Preview deployments

Host Test Results

    1 files      1 suites   1h 40m 19s ⏱️
3 186 tests 3 170 ✅ 15 💤 0 ❌ 1 🔥
3 205 runs  3 188 ✅ 15 💤 1 ❌ 1 🔥

Results for commit 9c2642e.

For more details on these errors, see this check.

Realm Server Test Results

    1 files      1 suites   10m 46s ⏱️
1 733 tests 1 733 ✅ 0 💤 0 ❌
1 826 runs  1 826 ✅ 0 💤 0 ❌

Results for commit 9c2642e.

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.

1 participant