Skip to content

perf(charges-matcher): stream queue match suggestions with @defer (3/3)#3984

Draft
gilgardosh wants to merge 2 commits into
mainfrom
claude/charges-await-match-queue-perf-3-defer
Draft

perf(charges-matcher): stream queue match suggestions with @defer (3/3)#3984
gilgardosh wants to merge 2 commits into
mainfrom
claude/charges-await-match-queue-perf-3-defer

Conversation

@gilgardosh

Copy link
Copy Markdown
Collaborator

Context

PR 3 of 3 (final) in the chargesAwaitingMatchQueue performance series. Stacked on #3983 (base is that PR's branch; retarget down the stack to main as the earlier PRs merge).

  1. perf(charges-matcher): build match candidate pool once per queue request (1/3) #3982 — shared candidate pool
  2. perf(charges-matcher): score match candidates in parallel (2/3) #3983 — parallelize the per-candidate scoring loop
  3. This PR — @defer the match suggestions so base charges paint immediately

Draft: opened as a draft because this PR changes generated GraphQL contracts (a new codegen mapper) and I could not run yarn generate, typecheck, or the test suite in the execution environment — yarn install is blocked by the org egress policy (the xlsx dep resolves from cdn.sheetjs.com, 403). Please run yarn generate && yarn lint && yarn test:integration and the client build before marking ready.

Problem

Even with PRs 1–2 the queue was still computed eagerly: the resolver scored every charge on the page before returning anything, so the client couldn't render until all scoring finished.

Change

Make match suggestions lazy and stream them via @defer.

Server

  • ChargeWithSuggestions.suggestions is now a field resolver, backed by a per-operation DataLoader (suggestionsByChargeIdLoader) that coalesces every requested charge into one findMatchesForCharges call — so the shared candidate pool from perf(charges-matcher): build match candidate pool once per queue request (1/3) #3982 is still built once.
  • New codegen mapper ChargeWithSuggestions → ChargeWithSuggestionsMapper makes suggestions optional on the parent.
  • BY_DATE returns the page unscored; suggestions resolve lazily. BY_SCORE still evaluates the window up front (scores are required to sort) and carries the suggestions on each item, so the field resolver returns them directly.

Client

  • The ChargesAwaitingMatchQueue query wraps suggestions in a ... on ChargeWithSuggestions @defer inline fragment (mirroring the existing ChargeMetadata @defer pattern).
  • The queue list paints immediately: the initial skeleton is gated on fetching && !data (the repo's deferred-query convention), and the Suggested-Match pane shows a per-card "Scoring suggestions…" state until each charge's deferred payload arrives.

Files

  • codegen.ts, charges-matcher/types.ts — new mapper.
  • providers/queue-match-evaluator.provider.ts — batching suggestions loader.
  • resolvers/index.tssuggestions field resolver.
  • resolvers/charges-awaiting-match-queue.resolver.tsBY_DATE returns unscored page.
  • client/.../queue-query.graphql.ts, client/.../index.tsx@defer + loading state.
  • Tests updated for the deferred BY_DATE behavior + new loader test.

Testing

⚠️ Not run locally (see draft note). Needs: yarn generate (mapper → resolver + client types), server unit/integration tests, and a manual pass on the matching screen confirming base charges render immediately with suggestions streaming in.

🤖 Generated with Claude Code


Generated by Claude Code

@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 20, 2026 07:37 — with GitHub Actions Inactive
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 20, 2026 07:37 — with GitHub Actions Inactive

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request implements lazy loading and streaming of charge-match suggestions using GraphQL @defer and a server-side DataLoader. This allows the client to render base charges immediately while suggestions load in the background. The review feedback points out a critical issue where a deferred payload error could unmount the entire UI, and suggests adjusting the error handling condition to keep the UI visible when data is present.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

)}

{fetching ? (
{fetching && !data ? (

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.

high

With the introduction of @defer for streaming suggestions, a GraphQL error in any of the deferred payloads will populate the error object. Currently, if error is truthy, the entire main UI is unmounted and replaced with null (line 165: ) : error ? null : (). This means a single deferred scoring failure will cause the already-rendered base charges to disappear, defeating the purpose of lazy loading.

To prevent this, we should still render the main UI if data is present, even if there is an error (which will still be displayed in the Alert at the top). Consider changing line 165 to check !data instead of error:

      ) : !data ? null : (

@gilgardosh
gilgardosh force-pushed the claude/charges-await-match-queue-perf-2-scoring branch from dd28ef4 to 203d4ce Compare July 20, 2026 09:53
@gilgardosh
gilgardosh force-pushed the claude/charges-await-match-queue-perf-3-defer branch from d00be5a to 606c4ec Compare July 20, 2026 09:53
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 20, 2026 09:53 — with GitHub Actions Inactive
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 20, 2026 09:54 — with GitHub Actions Inactive
@gilgardosh
gilgardosh force-pushed the claude/charges-await-match-queue-perf-2-scoring branch from 203d4ce to be0ff13 Compare July 20, 2026 10:03
@gilgardosh
gilgardosh force-pushed the claude/charges-await-match-queue-perf-3-defer branch from 606c4ec to 3fffc51 Compare July 20, 2026 10:03
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 20, 2026 10:03 — with GitHub Actions Inactive
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 20, 2026 10:03 — with GitHub Actions Inactive
@gilgardosh
gilgardosh force-pushed the claude/charges-await-match-queue-perf-2-scoring branch from be0ff13 to 72f4140 Compare July 20, 2026 11:24
@gilgardosh
gilgardosh force-pushed the claude/charges-await-match-queue-perf-3-defer branch from 3fffc51 to 666d64f Compare July 20, 2026 11:28
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 20, 2026 11:28 — with GitHub Actions Inactive
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 20, 2026 11:28 — with GitHub Actions Inactive
@gilgardosh gilgardosh self-assigned this Jul 20, 2026
Base automatically changed from claude/charges-await-match-queue-perf-2-scoring to main July 20, 2026 11:35
claude and others added 2 commits July 20, 2026 14:55
Make `ChargeWithSuggestions.suggestions` a lazy field resolver so the awaiting-
match queue can return base charges without scoring them up front. A new
per-operation DataLoader coalesces all suggestion loads into a single
`findMatchesForCharges` call, so the shared candidate pool is still built once.

- Server: add a `ChargeWithSuggestions` codegen mapper (suggestions optional),
  a `suggestions` field resolver, and the batching loader. `BY_DATE` returns the
  page unscored; `BY_SCORE` still evaluates up front (needed to sort) and carries
  the suggestions on each item.
- Client: wrap `suggestions` in a deferred inline fragment so the queue list
  paints immediately, gating the initial skeleton on `fetching && !data` and
  showing a per-card "Scoring suggestions…" state until the deferred payload
  arrives.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PVAbrnSqJcoqq8BRyDStKY
@gilgardosh
gilgardosh force-pushed the claude/charges-await-match-queue-perf-3-defer branch from 666d64f to c98f6d9 Compare July 20, 2026 11:56
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 20, 2026 11:56 — with GitHub Actions Inactive
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 20, 2026 11:56 — with GitHub Actions Inactive
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Snapshot Release (alpha)

The latest changes of this PR are available as alpha on npm (based on the declared changesets):

Package Version Info
@accounter/client 0.1.0-alpha-20260720115807-16e3e4b6d6d1e5fc9ba80097298a34fb9fd69bb1 npm ↗︎ unpkg ↗︎
@accounter/gmail-listener 0.1.3-alpha-20260720115807-16e3e4b6d6d1e5fc9ba80097298a34fb9fd69bb1 npm ↗︎ unpkg ↗︎
@accounter/israeli-vat-scraper 0.1.13-alpha-20260720115807-16e3e4b6d6d1e5fc9ba80097298a34fb9fd69bb1 npm ↗︎ unpkg ↗︎
@accounter/modern-poalim-scraper 0.10.7-alpha-20260720115807-16e3e4b6d6d1e5fc9ba80097298a34fb9fd69bb1 npm ↗︎ unpkg ↗︎
@accounter/scraper-app 0.0.3-alpha-20260720115807-16e3e4b6d6d1e5fc9ba80097298a34fb9fd69bb1 npm ↗︎ unpkg ↗︎
@accounter/server 0.2.0-alpha-20260720115807-16e3e4b6d6d1e5fc9ba80097298a34fb9fd69bb1 npm ↗︎ unpkg ↗︎
@accounter/shaam-uniform-format-generator 0.2.7-alpha-20260720115807-16e3e4b6d6d1e5fc9ba80097298a34fb9fd69bb1 npm ↗︎ unpkg ↗︎
@accounter/shaam6111-generator 0.1.9-alpha-20260720115807-16e3e4b6d6d1e5fc9ba80097298a34fb9fd69bb1 npm ↗︎ unpkg ↗︎

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.

2 participants