A minimal AlphaGenome Atlas client — the blueprint, test-proven¶
Adopted on 2026-09-10 by RM192. The code left this directory and now lives as
enricher/src/just_dna_enricher/atlas_client.py (the client), atlas_protos.py (the generator, run
as just-dna-enricher atlas generate) and enricher/tests/test_atlas_client.py (the tests, now
inside testpaths). The dependencies are the [atlas] extra, and the alphagenome extra is gone.
This file stays as the record of what the blueprint cost and what it refused, which is the half
that does not travel with the code; everything below describes the probe as it stood, in the past
tense as of that date.
Not a shipped surface, and not adopted. Nothing in just-dna-enricher imports this, it sits
outside testpaths so uv run pytest does not collect it, and no RMn covers it. It exists to
turn one sentence of ALPHAGENOME_ATLAS.md § 6.2 into something a test
can fail: the Atlas service is reachable on grpcio + protobuf alone.
That claim was first written here as "a 22 MB client exists and is not declarable". Reading the
upstream repository showed the second half was wrong — github.com/google-deepmind/alphagenome is
Apache-2.0 and ships the .proto sources its own wheel generates bindings from, so the light path
is declarable after all. This directory is what that costs.
Run it¶
uv run --with grpcio-tools python docs/probes/alphagenome_poc/generate.py
uv run --with grpcio-tools pytest docs/probes/alphagenome_poc/ -vvv
Twenty-one of the twenty-five tests need no network and no key. The four live ones need both
ALPHAGENOME_API_KEY and the repo's opt-in switch JUST_DNA_NETWORK_TESTS=1
(@network-tests-optin); they are skipped otherwise.
What it costs¶
| declared runtime dependencies | grpcio, protobuf |
| build-time only | grpcio-tools, declared nowhere |
| vendored source | 28 KB — three Apache-2.0 .proto files in docs/vendor/alphagenome_protos/ |
| installed size | 22 MB, against 255 MB for uv add alphagenome |
| decode | struct.unpack from the standard library — not even numpy |
What the tests actually pin¶
Five of them carry the argument; the rest keep the client honest.
test_imports_stay_within_the_declared_floorwalks the client's AST and asserts its third-party imports are exactly{grpc, docs}. An AST walk rather than asys.modulescheck, because by the time the module is imported another test's heavier import would already be resident and the assertion would pass for the wrong reason. This is what makes "22 MB, not 255 MB" a property of the code instead of a claim in prose.test_bindings_regenerate_from_the_vendored_sourcesregenerates into a scratch directory. The repository carries.protosources, not generated code, so the bindings are reproducible from what is committed rather than from what a wheel happened to ship.test_the_generated_bindings_do_not_shadow_the_upstream_packageis the one that came out of a real mistake. protoc bakes the staged path into every cross-import, so staging at upstream's ownalphagenome/protos/produces a package literally namedalphagenome— which shadows the real wheel for anyone who installs both, and fails with an import error far from its cause. The sources are staged under the full package path instead, so the cross-imports readfrom docs.probes.alphagenome_poc.generated._alphagenome_atlas_protos import …: unambiguous, unshadowable, and importable without asys.pathinsertion.test_the_api_reproduces_the_downloaded_fileis the finding that matters most for adoption. It asserts the Atlas returns what the 88.5 GB AVI artifact contains —rawto 5e-6 and the derivedPHREDto 1e-4 — so the download and the RPC are one source. If it ever fails, they have diverged and § 6.4 needs re-measuring.test_the_api_saturates_where_the_download_still_has_a_valueis its counterweight, and it came out of measuring the AVI file whole.calibrated_scoresis afloat32, so the largest quantile it can carry caps a derived Phred at 72.247 — while the published artifact reaches 89.451. Above the cap the API returns exactly1.0and the rank is unrecoverable. It affects about 1,300 rows genome-wide, which are precisely the highest-impact ones. An earlier version of this client clamped at the FAQ's Phred 50 and would have rewritten those as fifty; the clamp is gone, and a saturated quantile now raisesAtlasNotScoredwhileVariantScore.phredreturnsNone.
The three-valued part¶
The error hierarchy is not decoration. The service says no in three different ways and each has a different remedy:
| what happened | type | remedy |
|---|---|---|
| transport failed | AtlasUnavailable |
retry |
REF disagrees with GRCh38 |
AtlasRefMismatch |
fix the caller's data — and the server names the real base |
| an indel | AtlasNotScored |
none: the answer does not exist |
| a quantile saturated at 1.0 | AtlasNotScored |
read PHRED from the downloaded file instead |
AtlasNotScored deliberately does not derive from AtlasRefused, so an except AtlasRefused
cannot swallow it. A caller that recorded "no score" for a variant the service never claimed to
have scored would be @unreachable-not-absent in one line, and
test_not_scored_is_not_a_refusal is what stops it. test_handler_order_is_not_load_bearing_by_accident
enumerates the ladder, because AtlasRefMismatch being a subclass makes a caller's except
order load-bearing (@client-exception-contract).
What it does not do¶
- No interval RPC.
ListDenseVariantScoresneeds anx-goog-fieldmaskheader and 32 bp chunking; hand-built requests returnedINVALID_ARGUMENTand the § 6.5 motif measurement was taken through the SDK instead. Adding it here is the obvious next step and was not taken, since nothing needs it yet. - No retry loop. The vendored
grpc_service_config.jsoncarries upstream's policy and is passed to the channel, but nothing here layerstenacityon top the way the enricher's own clients do (@retry-attempt-floor). Adoption would. - No caching, no rate limiting, no
SourceRow. Every one of those is a real requirement for a shipped enricher lane (@write-the-sourcerow,@shared-pacing-gate) and every one is absent, because this proves reachability and nothing else.
The cost this blueprint hides¶
Generated code has no compile-time signal when the service's protos move. Upstream regenerates on
every release; a vendored copy goes stale silently, and the failure surfaces as a decode error or a
missing field rather than a build break. The blueprint answered that with a PROVENANCE.txt
recording the commit (aa6fc8f, 2026-09-08) so re-vendoring was a diff — and RM196 later removed
the vendored copy entirely: the repository carries a commit id and a sha256 per file, and
atlas_protos.fetch_protos() refuses anything that does not match. Noticing a re-pin is due is
still manual. That is the price of the light path, and it is the reason § 6.2 listed four shapes and
picked none.