Skip to content

SCRUM-6436 preserve the sequence viewer for standalone variants - #1665

Merged
christabone merged 3 commits into
stagefrom
scrum-6436-standalone-variant-viewer-ids
Aug 20, 2026
Merged

SCRUM-6436 preserve the sequence viewer for standalone variants#1665
christabone merged 3 commits into
stagefrom
scrum-6436-standalone-variant-viewer-ids

Conversation

@christabone

Copy link
Copy Markdown
Contributor

Summary

  • retain the bounded projected allele-identifier results introduced by SCRUM-6436 bound allele viewer payloads #1664
  • add supplementalData.hasStandaloneVariants using the same gene and table filters
  • detect standalone mapped variants with a one-record category projection instead of returning their high-cardinality HGVS set
  • document the supplemental response contract for agr_ui#1875

Why

Jim identified that Rat RGD:2917 has 916 standalone variant rows but zero mapped allele IDs. The old UI mounted the viewer because those rows have non-empty variantList values; the new UI incorrectly interpreted the empty ID result as no viewer content.

The stage alias also contains genes with roughly 99,000 standalone/high-throughput variant rows. Returning every HGVS value would create unnecessary paging for records that are not in the embedded allele-associated VCF. A bounded presence signal restores viewer mounting without compromising the OOM fix.

Stage evidence

  • RGD:2917: 0 mapped allele IDs, 916 standalone mapped variants
  • RGD:2332: 6 mapped allele IDs, 1,355 standalone mapped variants
  • representative MGI, FB, WB, ZFIN, and RGD queries confirm the two scopes remain filter-consistent

Verification

  • 10 focused API tests pass: GeneControllerTest, AlleleESServiceTest, ESServiceSourceProjectionTest
  • source projections remain bounded: allele results use the existing identifier allowlist; standalone detection fetches at most one category field
  • paired UI head: SCRUM-6438 use projected allele viewer identifiers agr_ui#1875 commit 82edbfa3

@christabone

Copy link
Copy Markdown
Contributor Author

@claude Please review this focused follow-up to merged PR #1664 together with alliance-genome/agr_ui#1875 at 82edbfa3. Check that supplementalData.hasStandaloneVariants exactly preserves viewer mounting for filtered standalone mapped variants, that its one-record projection does not reintroduce the original payload problem, and that all table filters remain consistent between the identifier and presence queries. Please also challenge the design against high-cardinality HTP genes and the paging contract.

@claude

claude Bot commented Aug 20, 2026

Copy link
Copy Markdown

Claude finished @christabone's task in 6m 56s —— View job


Review: SCRUM-6436 — supplementalData.hasStandaloneVariants

  • Gather context (diff vs origin/stage, PR SCRUM-6436 bound allele viewer payloads #1664 baseline, ES mappings, SearchDAO)
  • Verify the flag preserves viewer mounting semantics
  • Confirm the one-record projection doesn't reintroduce the payload problem
  • Check table-filter consistency between the identifier and presence queries
  • Read the paired UI head agr_ui#1875 (useViewerAlleleIds.js, fetchAllPages.js, alleleTable.jsx)
  • Challenge the design against HTP genes and the paging contract
  • Run the focused tests

Verdict: the approach is sound and the OOM fix from #1664 is intact. The projection is genuinely bounded, and the filter sets are consistent. I'd hold on merging for #1 (per-page repetition) and #5 (probe can 500 the whole endpoint); the rest are test-coverage and polish.

Tests run locally with JDK 21 — AlleleESServiceTest: 7/7 pass (mvn -pl agr_api -am -Dtest=AlleleESServiceTest test → BUILD SUCCESS).


Direct answers to your three questions

Does the one-record projection reintroduce the payload problem? No. hasStandaloneVariants builds its own Pagination(1, 1, null, null) with sourceIncludes = ["category"] (AlleleESService.java:252-253), so SearchDAO.performQuery issues size: 1, from: 0, _source: ["category"]. Worst case is one ~20-byte source document regardless of whether the gene has 916 or 99,000 standalone rows. The caller's pagination is untouched by the probe. The residual cost is in the count, not the fetch — see #2.

Are the table filters consistent? Yes. Both queries take the identical addTableFilter(pagination, …) over the same eight filterOptionMap entries set in GeneController.java:138-146 — which are exactly the eight set for getAllelesPerGene (lines 100-108), so the probe mirrors the main table's filter scope. I checked that no filter silently no-ops on one side: symbol, variantList.curatedVariantGenomicLocations.hgvs, variantList.variantType.name, …vepConsequences.name, hasDisease, hasPhenotype, and alterationType are all mapped on both allele_summary and variant_summary docs (Mapping.java:91,188,303-309; VariantMapping.java:19,27,45-46). addFilterOption skips empty values, so an unfiltered request adds nothing to either query.

The one asymmetry — the identifier query intersects alterationType.keyword with VISIBLE_ALTERATION_TYPES and the probe doesn't — is correct: a user-selected category naming a standalone alteration type yields 0 identifiers and a true probe, which is precisely the RGD:2917 shape.

Does it exactly preserve viewer mounting? Almost — two deliberate deltas (see #3). Neither looks like a regression, but "exactly preserves" is a slight overclaim in the PR body.


Findings

1. The probe runs on every page, and the extra work lands hardest on the genes it can't help (moderate)

AlleleESService.java:240 calls hasStandaloneVariants unconditionally. The UI caps at PUBLIC_API_PAGE_SIZE = 1000 (fetchAllPages.js) and the API caps at ALLELE_VIEWER_MAX_LIMIT = 1000, so a gene with 60k visible alleles means 60 sequential requests → 60 identical standalone probes, 59 of them discarded: fetchAllPages keeps supplementalData from page 1 only ({...firstPage, results}), and hasViewerContent short-circuits on Boolean(response?.results?.length) before ever reading the flag.

So the probe is pure overhead on exactly the high-cardinality genes where the identifier query is already 60 round trips, and provably irrelevant there (non-empty results|| never evaluates it).

if (pagination.getPage() == null || pagination.getPage() == 1) {
    response.addSupplementalData("hasStandaloneVariants", hasStandaloneVariants(geneId, pagination.getFilterOptionMap()));
}

This is a no-op for the UI consumer (which already reads page 1 only) and honest for direct API users provided the OpenAPI description says so — see #10.

Fix this →

2. A boolean presence check pays for an exact count (moderate)

SearchDAO.performQuery hardcodes searchSourceBuilder.trackTotalHits(true) (SearchDAO.java:81), so on your ~99,000-row HTP gene the probe counts all 99,000 matching docs to answer > 0. It's a filter-only bool so ES is fast at it, but it's the wrong shape for the question. Three tightenings, cheapest first:

  • new Pagination(1, 0, null, null) instead of (1, 1, …)size: 0, no _source fetch at all, and the sourceIncludes line becomes unnecessary. This makes "does the projection reintroduce the payload problem" unanswerable-by-construction rather than answered-by-inspection.
  • There is already a purpose-built SearchDAO.performCountQuery(query) (size(0); trackTotalHits(true)) that isn't exposed through ESService. A small protected long getTotalHits(BoolQueryBuilder) wrapper would be more idiomatic than hand-rolling a one-doc probe.
  • queryBuilder.must(QueryBuilders.termQuery("geneIds", geneId)).filter(...) at line 246, so the clause is unscored and filter-cacheable. Same applies to line 220 (pre-existing from SCRUM-6436 bound allele viewer payloads #1664, worth folding in).

Real fix is track_total_hits: 1 / terminate_after: 1, which SearchDAO doesn't currently expose. With page-1 gating from #1, the exact count becomes once-per-gene-view and this drops to a nit.

3. "Exactly preserves viewer mounting" — two deliberate deltas (worth confirming, not blocking)

The old condition was allelesFiltered.data.results.flatMap(a => a.variantList || []).length > 0 over the first 1000 rows of the filtered /alleles table (deleted useAllVariants.js pinned sizePerPage: 1000).

  • Delta A (a fix): the old check was truncated at row 1000. Where sorting pushed all variant-bearing rows past 1000, the old UI did not mount; the new results.length > 0 is unbounded and does. Strictly better.
  • Delta B (a narrowing): the probe adds existsQuery("variantList.curatedVariantGenomicLocations.hgvs") (line 249), which the main table query does not apply — getAllelesByGene lines 186-189 accept any variant_summary with a null allele. So a standalone row carrying a variantList but no curated genomic location mounted the viewer before and won't now. Probably the right call (nothing to draw without a location), but it makes the probe deliberately narrower than the table it mirrors, and it's the one place the "exactly preserves" claim doesn't hold. Since you have stage access: do RGD:2917's 916 rows all carry hgvs, and is there any MOD with standalone rows that lack it? Also note exists on a text field won't distinguish a missing location from one with an empty-string hgvs.

4. The probe can take down a payload that already succeeded (moderate)

SearchDAO.performQuery swallows IOException and returns null, so line 255 NPEs, GeneController catches, and the endpoint 500s. A transient failure on a supplemental signal now discards an identifier list that was computed successfully one line earlier. Before this PR the endpoint had one such failure point; now it has two and the second is optional. Suggest:

try {
    response.addSupplementalData("hasStandaloneVariants", hasStandaloneVariants(geneId, pagination));
} catch (RuntimeException e) {
    log.warn("Standalone variant presence check failed for gene {}; omitting viewer hint", geneId, e);
}

Fail-open (true) is also defensible if a spuriously-mounted viewer beats a spuriously-hidden one — your call, but the current fail-by-500 is the worst of the three.

Fix this →

5. The PR's central claim has no test (moderate)

CapturingAlleleESService only records capturedPagination when responseIndex == 0 (AlleleESServiceTest.java:196-198), so nothing asserts the probe is bounded. A future refactor that dropped setSourceIncludes(List.of("category")) — or bumped the limit — would pull full _source for a 99k-row gene and all seven tests would still pass green. Capture both paginations and assert the second has limit == 1 (or 0) and sourceIncludes == ["category"]. This is the assertion that actually protects the OOM fix.

6. A deleted assertion still holds and was worth keeping (minor)

assertFalse(query.contains("variant_summary")) was dropped along with the assertFalse import. It applies verbatim to capturedQueries.get(0) — the identifier query must never match standalone variants. It's the guard that keeps the two scopes from bleeding into each other, and now that there are two queries it's more valuable, not less. Re-add as assertFalse(identifierQuery.contains("variant_summary")).

7. No test for the motivating case (minor)

Neither test covers "0 identifiers + standalone hits present" — the RGD:2917 shape (response(0) for identifiers, response(916) for the probe → empty results, flag true). That's the exact regression this PR exists to fix. The existing hasStandaloneVariants == true assertion sits in a test with 2 identifiers, where the UI's || makes the flag irrelevant.

8. Smaller items

  • Coupling: hasStandaloneVariants(String geneId, Pagination filters) receives the pagination after line 224 mutated its sourceIncludes, and reads only getFilterOptionMap(). Passing the map directly (or an addTableFilter(Map, BoolQueryBuilder) overload) makes the dependency explicit and stops a future edit leaking viewer paging into the probe. The parameter name filters already hints at the intent.
  • Test double: responses[responseIndex++] throws a bare ArrayIndexOutOfBoundsException if a third query is ever issued. A guarded message would make that failure legible.
  • OpenAPI: the change is prose on the 200 description; supplementalData has no schema, so hasStandaloneVariants isn't discoverable from the generated spec. If page-1 gating lands, the description must say so, or a direct consumer paging from page 2 sees the field vanish with no explanation.

On the paired UI head (agr_ui#1875 @ 82edbfa3)

  • fetchAllPages takes supplementalData from page 1 via {...firstPage, results} — correctly page-invariant, and compatible with the page-1 gating in AGR-949 Increased boost on category:gene to 1.1 #1. ✅
  • hasViewerContent(taxonId, hasAlleles, response) returns hasAlleles for ISOFORM_ONLY_TAXA, where the query is enabled: false. In react-query v5 a disabled query reports isLoading === false, so the new viewerAlleleIds.isLoading guard in alleleTable.jsx doesn't permanently blank human/SGD genes. ✅
  • One to look at: getVisibleViewerAlleleIds returns selectionOverride.alleleIds unfiltered when override is active, whereas the deleted code applied the ALLELE_WITH_ONE_VARIANT/ALLELE_WITH_MULTIPLE_VARIANTS category filter to the override branch too. If a user can select a standalone-variant or non-viewer-category row, its ID now reaches allelesVisible. Worth a check against how selectionOverride.alleleIds is populated in useAlleleSelection.

On the HTP challenge

The design holds. The probe returns zero rows and is O(1) in payload no matter the cardinality, so nothing here walks back the #1664 fix. The two residual costs are #1 (per-page repetition) and #2 (exact count) — both real, neither an OOM risk, and #1's fix largely subsumes #2.

The open product question: for a gene with 99k standalone variants and 0 mapped alleles, the viewer mounts with allelesVisible: []. If the genomefeatures track can't source those variants — the PR says they're not in the embedded allele-associated VCF — the user gets a mounted-but-empty variant track instead of "No mapped variant information available". That is what the old UI did, so it's faithfully preserved; whether preserved is the same as right is worth one round with Jim before this ships.


Note: I could not run the full test suite — the sandbox has no Maven Central access and the default JDK is 17. I ran the focused test with JAVA_HOME=/usr/lib/jvm/temurin-21-jdk-amd64 and network enabled; GeneControllerTest and ESServiceSourceProjectionTest were not re-run.
· Branch: scrum-6436-standalone-variant-viewer-ids

@claude

claude Bot commented Aug 20, 2026

Copy link
Copy Markdown

Review — SCRUM-6436 standalone variant viewer signal

Scoped to the three changed Java files (one service method, one OpenAPI description, test updates). I traced the new query against the ES mappings, ESService.addTableFilter, and SearchDAO.performQuery.

The change looks correct. Specifics I checked:

  • existsQuery("variantList.curatedVariantGenomicLocations.hgvs") resolves correctly from the parent document context: variantList is declared as type: object (not nested) in both Mapping.java:299 and VariantMapping.java:41, and the hgvs leaf is explicitly mapped despite dynamic: false. An exists filter here would have silently returned false for every gene had the field been nested or unmapped.
  • The standalone predicate (category.keyword = variant_summary + mustNot exists allele) matches the clause getAllelesByGene already uses for standalone rows, so the viewer signal stays consistent with what the allele table renders.
  • No aliasing hazard between the two queries: getVisibleAlleleIdsByGene mutates the caller's Pagination via setSourceIncludes(VIEWER_SOURCE_INCLUDES), but hasStandaloneVariants reads only filterOptionMap from it and issues its own countPagination, so the identifier projection is unaffected and paging isn't perturbed. Offset math for new Pagination(1, 1, null, null) is from=0, size=1.
  • > 0 on getTotalHits().value is safe — SearchDAO sets trackTotalHits(true), so the value is never a sentinel.
  • Additive-only API change: supplementalData was previously null on this endpoint, and JsonResultResponse carries no per-endpoint @JsonView restriction that would drop it. No breaking change for existing consumers.
  • Filters that only apply to alleles (e.g. filter.synonymsallele.alleleSynonyms.displayText, filter.alleleCategoryalterationType.keyword) will zero out the standalone check when set. That mirrors the table's own behavior for those rows, so it reads as intended rather than a bug.

Low-severity note (non-blocking)

agr_api/src/main/java/org/alliancegenome/api/service/AlleleESService.java:252 — because SearchDAO.performQuery hardcodes trackTotalHits(true), this presence check pays for an exact count of every matching document (the PR cites genes with ~99k standalone variant rows) when the code only needs "at least one." It also re-runs on every page request even though the answer is page-invariant. Neither breaks production — it's one extra shard-local count per request — but if the viewer pages aggressively over large-variant genes, a trackTotalHitsUpTo(1) path on the DAO would make this materially cheaper.

Test note

The old assertion assertFalse(query.contains("variant_summary")) was dropped rather than retargeted. It's still a meaningful invariant for the identifier query specifically and could be kept as assertFalse(identifierQuery.contains("variant_summary")) — otherwise nothing guards against variant docs leaking back into the bounded ID result that #1664 fixed.

I did not execute the test suite (no populated Maven repository in this environment), so the 10-passing-tests claim in the description is unverified here.

@christabone

Copy link
Copy Markdown
Contributor Author

@claude Addressed the test note in 10e6285ee: the identifier-query test again asserts that variant_summary is absent, while the second captured query asserts the standalone scope. The focused service suite passes 7/7. The exact-count optimization remains intentionally deferred because it would broaden this three-file correction into the shared DAO; the representative six-gene stage query completed in about 222 ms and the current measured mapped-ID cardinality uses one page. Please confirm the updated head.

@claude

claude Bot commented Aug 20, 2026

Copy link
Copy Markdown

Claude finished @christabone's task in 2m 54s —— View job


Confirming head 10e6285ee

  • Check for new commits since my earlier review
  • Verify the re-added variant_summary assertion targets the identifier query
  • Re-run the focused suites locally

Confirmed. 10e6285ee addresses finding #6 from my earlier review correctly and nothing else regressed.


What the new commit does

AlleleESServiceTest.java:84 re-adds assertFalse(identifierQuery.contains("variant_summary")) — retargeted to capturedQueries.get(0) rather than the old undifferentiated query, which is exactly the right scoping now that two queries are issued. Paired with the existing assertTrue(standaloneVariantQuery.contains("variant_summary")) at line 87, the two scopes are now pinned in opposite directions in the same test, which is stronger than the pre-#1664 single assertion. The assertFalse import is restored at line 4.

Test results (run locally, JDK 21)

Suite Result
AlleleESServiceTest 7/7 pass
ESServiceSourceProjectionTest 2/2 pass
GeneControllerTest 1/1 pass

BUILD SUCCESS on both invocations — the 10-test claim in the PR description is verified.

On the deferred count optimization

Accepted, and I think the reasoning is right. My findings #1 (per-page repetition) and #2 (exact count for a boolean) both scaled with page count, and your measurement — mapped-ID cardinality fits in one page for the representative genes, six-gene stage query at ~222 ms — collapses both to a single probe per gene view. At that shape, track_total_hits on a filter-only bool is not worth pulling the shared SearchDAO into a three-file correction. If a gene ever crosses 1000 mapped IDs, the page-1 gate is still the cheap fix and stays a one-liner in AlleleESService.

Two items from my earlier review that this commit doesn't touch

Neither blocks; flagging so the deferral is deliberate rather than incidental.

  1. The probe's boundedness still has no assertion (my AGR-1002 add automatedGeneSynopsis to GeneDocument #5). CapturingAlleleESService records capturedPagination only when responseIndex == 0 (lines 198-200), so a future edit dropping setSourceIncludes(List.of("category")) or raising the limit at AlleleESService.java:252-253 would pull full _source on a 99k-row gene with all 7 tests still green. Capturing both paginations and asserting limit == 1 / sourceIncludes == ["category"] on the second is the assertion that actually guards the SCRUM-6436 bound allele viewer payloads #1664 fix — a smaller change than the one you just made.

  2. A probe failure still 500s a payload that already succeeded (my Added OpenAPI spec to wildfly AGR-886 #4). SearchDAO.performQuery swallows IOException and returns null, so AlleleESService.java:255 NPEs and discards the identifier list computed one line earlier. A try/catch around line 240 that logs and omits the supplemental field keeps a transient failure on an optional hint from taking down the required result.

Fix #5 → · Fix #4 →

The still-open product question from my first review — a gene with standalone variants and zero mapped alleles mounts the viewer with allelesVisible: [] — is unchanged by this commit and is a conversation with Jim, not a code change.
· Branch: scrum-6436-standalone-variant-viewer-ids

@christabone

Copy link
Copy Markdown
Contributor Author

@claude Addressed the remaining boundedness test gap in 3a62e2218: the test double now captures both paginations and asserts the standalone probe is page 1, limit 1, with _source limited to category. The full focused set passes 10/10. I am deliberately not swallowing probe failures: hasStandaloneVariants is required for correct mounting when IDs are empty, and the UI uses staleTime: Infinity; returning 200 without the signal could cache an incorrect hidden viewer for the session. A failed request remains retryable and avoids representing an incomplete answer as successful. Please confirm the latest head and this disposition.

@claude

claude Bot commented Aug 20, 2026

Copy link
Copy Markdown

Claude finished @christabone's task in 3m 28s —— View job


Confirming head 3a62e2218

  • Check new commits since my last review
  • Verify the boundedness assertions on the probe pagination
  • Re-run the focused suites
  • Respond on the fail-fast disposition

Confirmed. 3a62e2218 closes finding #5, and I agree with the fail-fast disposition — it's the only one of the three options that can't cache a wrong answer.


The new assertion actually bites

AlleleESServiceTest.java:60-68 now pins both paginations, and CapturingAlleleESService records every one it receives (line 204) instead of only the first. The split into identifierPagination / standaloneProbePagination also incidentally proves the probe uses a distinct object from the caller's — the two assert different sourceIncludes, so a future edit that reused the caller's pagination would fail rather than silently leak viewer paging into the probe.

I mutation-tested it rather than taking the green run at face value. Reverting the two bounding lines at AlleleESService.java:252-253 (drop setSourceIncludes, raise the limit to 1000) makes the suite fail:

AlleleESServiceTest.returnsOnlyProjectedIdentifiersAndPreservesCompleteTotal:66
  expected:<[category]> but was:<[]>

That is the regression the #1664 fix needed a guard against, and it's now caught. Working tree restored, no changes pushed.

Test results (run locally, JDK 21)

Suite Result
ESServiceSourceProjectionTest 2/2 pass
AlleleESServiceTest 7/7 pass
GeneControllerTest 1/1 pass
Total 10/10, BUILD SUCCESS

On not swallowing probe failures — agreed, and for a sharper reason than "don't hide errors"

Your argument is correct and I'll withdraw my #4 suggestion. The decisive point is that omission is indistinguishable from a genuine false on the wire. hasViewerContent reads the flag only when results.length === 0 — precisely the RGD:2917 shape — and an absent field is falsey there. So a 200-with-omitted-field is not a degraded answer, it's a wrong answer wearing a success code, and staleTime: Infinity makes it wrong for the rest of the session with no event that would refetch it.

Laying out all three options against that:

Disposition Transient probe failure produces Recoverable?
Omit field (my #4) viewer hidden on a gene that should show it no — cached for the session
Fail open (true) empty viewer mounted on a gene with no content no — cached for the session
Fail fast (current) error state yes — errors bypass staleTime and refetch on remount/retry

Both non-fail-fast options cache a wrong answer; only fail-fast produces a state react-query will ever re-ask about. Given that the flag is load-bearing exactly when the required result is empty, "partial success" isn't a meaningful state for this endpoint — it degrades the same field it exists to deliver. Fail-fast is right.

Two things that make it hold up in practice, both of which I checked:

  • The failure is observable, not silent: GeneController.getAlleleViewerIds catches, logs "Error while retrieving allele viewer identifiers" with the stack, and throws RestErrorException with a structured message (GeneController.java:155-160). A probe failure shows up in logs rather than as a mystery 500.
  • The failure shape is uniform with the identifier querySearchDAO.performQuery returning null on IOException already NPEs at AlleleESService.java:226 for the primary query, so the probe doesn't introduce a new failure semantics, just a second instance of the existing one. Nothing to reconcile.

Purely optional polish, not worth a commit on its own: the fail-fast path is reached via NPE-on-null rather than an explicit check, so the thrown type is incidental to the design. If SearchDAO's null-on-IOException contract is ever tightened, an explicit if (searchResponse == null) throw new IllegalStateException(...) at both call sites would state the intent. Today it behaves identically.


Remaining open items (all previously deferred, none blocking)

  1. Per-page repetition (my AGR-949 Increased boost on category:gene to 1.1 #1) — deferred on your measurement that mapped-ID cardinality fits one page for representative genes, which collapses it to one probe per gene view. Still a one-liner page-1 gate in AlleleESService if a gene ever crosses 1000 IDs.
  2. Exact count for a boolean (my add associationType column #2) — deferred to avoid pulling shared SearchDAO into a three-file correction. Agreed; ~222 ms on the six-gene stage query is not a problem worth that blast radius.
  3. Delta B (my Added fail on all errors #3) — the probe's existsQuery("variantList.curatedVariantGenomicLocations.hgvs") is narrower than the table query, which accepts standalone rows with any variantList. Worth the stage check on whether all 916 of RGD:2917's rows carry hgvs, since that's the one place "exactly preserves" doesn't literally hold. Not a code change.
  4. UI: getVisibleViewerAlleleIds override branch (agr_ui#1875) — returns selectionOverride.alleleIds without the ALLELE_WITH_*_VARIANT category filter the deleted code applied. Depends on how useAlleleSelection populates it.
  5. Product question — a gene with standalone variants and zero mapped alleles mounts the viewer with allelesVisible: []. Faithful to the old UI; whether faithful is right is a conversation with Jim, not a code change.

From my side the Java change is ready. Nothing in 3a62e2218 regressed, and the OOM fix from #1664 is now protected by a test that fails when the projection is removed.
· Branch: scrum-6436-standalone-variant-viewer-ids

@christabone

Copy link
Copy Markdown
Contributor Author

@oblodgett The focused post-#1664 follow-up is fully updated at 3a62e2218 and ready for your review. It keeps results/total limited to the existing mapped allele-ID contract and adds only supplementalData.hasStandaloneVariants, computed with the same table filters via an independent page-1, limit-1, category-only ES projection. This preserves viewer mounting for standalone-only cases such as RGD:2917 without returning the potentially ~99k standalone/HTP HGVS set or changing the VCF visibility filter.

Review-driven hardening completed:

  • identifier and standalone query scopes are asserted in opposite directions;
  • the one-record/category-only probe bound is regression-tested;
  • 10/10 focused tests pass;
  • Checkstyle, compile/package, Docker build, and security checks are green;
  • Claude mutation-tested the projection assertion and confirmed the change ready;
  • direct stage audit confirms all 916 RGD:2917 standalone rows have HGVS, and there are zero allele_summary documents with a variantList outside the one/multiple-variant alteration types.

Paired UI PR alliance-genome/agr_ui#1875 is green and Claude-marked LGTM at 82edbfa3. We retained fail-fast behavior for a probe error because a 200 response with an omitted/guessed flag would be cached indefinitely by the UI as a wrong mounting answer. Please let me know if you want any adjustment.

@christabone
christabone marked this pull request as ready for review August 20, 2026 21:04
@christabone
christabone requested a review from a team as a code owner August 20, 2026 21:04
@christabone
christabone requested a review from oblodgett August 20, 2026 21:04
@christabone
christabone merged commit 1ec0027 into stage Aug 20, 2026
5 checks passed
@christabone
christabone deleted the scrum-6436-standalone-variant-viewer-ids branch August 20, 2026 21:16
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