You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Moves the served data release from v24 to v25 and fixes a schema-discovery gap that v25's new column exposed.
Serving IDC v25
idc-index 0.12.5 → 0.13.0, pulling idc-index-data 24.2.2 → 25.0.0. That release is the data bump plus CI chores — no upstream API change. /v3/version and the MCP get_idc_version now report v25: 179 collections, 26 analysis results, 1,044,191 series, 99.9 TB, 242 DOIs (up from 237).
Schema drift across all 17 tables is two columns: analysis_results_index gains provenance, and prior_versions_index drops gcs_bucket_1 (unreferenced here).
Struct columns were undiscoverable
v25's provenance is a struct, and get_table_schema described it as the bare type RECORD — which names no field a caller could select. The same was already true of collections_index.sources, a RECORD[] whose elements nest two further structs (license, provenance). Writing SELECT provenance.data_contributor meant guessing field names, which is precisely what this server tells callers not to do.
The upstream schema JSON ships no fields for a RECORD, so those names exist nowhere but the Parquet footer. get_table_schema now reads them from there and advertises the real type:
Only RECORD columns are substituted. The rest keep their BigQuery-style names, which numeric_range_attributes() matches on.
A repeated record's DuckDB type already ends in [], so the mode is not applied on top of it (no [][]).
Specialized indices have no local Parquet until fetched, so they degrade to the un-expanded RECORD rather than failing discovery.
The runtime path needed no change — structs already serialized correctly, and dot access and UNNEST already worked.
provenance is deliberately left reachable via SQL only; it is not promoted into the typed AnalysisResult / CollectionDetail responses.
Testing
103 pass with the full specialized index set; 92 pass (10 skipped) on a bundled-only build, covering the degradation path.
Five new tests in tests/test_struct_columns.py pin the expansion, the no-double-[] rule, selectability, serialization, and the missing-Parquet fallback.
Both SQL examples added to the docs were run verbatim.
ruff check / ruff format clean; bandit shows only pre-existing nosec notes.
Docs
Struct guidance sits beside the existing array guidance in docs/user-guide.md and is mirrored into the idc://guide MCP resource. INSTRUCTIONS is untouched — it stays lean. Changelog entry added under [Unreleased].
The version bump to 3.0.0b5 is not in this PR: the runbook requires it be its own commit, cut after this merges.
Pulls idc-index-data 25.0.0, moving the served data release from v24 to v25:
179 collections, 26 analysis results, 1,044,191 series, 99.9 TB.
idc-index 0.13.0 is the data bump plus CI chores; no API surface changed.
Schema drift across all 17 tables is limited to two columns:
analysis_results_index gains `provenance`, and prior_versions_index drops
`gcs_bucket_1` (unreferenced here).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
get_table_schema / GET /v3/tables/{table} rendered a struct column as the
bare type `RECORD`, which names no field a caller could select. v25's new
analysis_results_index.provenance is a struct, as is the pre-existing
collections_index.sources (whose elements nest two further structs), so
writing `SELECT provenance.data_contributor` meant guessing the field
names — the one thing this server tells callers not to do.
The upstream schema JSON carries no `fields` for a RECORD, so the names
exist nowhere but the Parquet footer; read them from there and advertise
the real DuckDB type, e.g.
STRUCT(data_contributor VARCHAR, source_data_provider VARCHAR, ...)
Only RECORD columns are substituted: the rest keep their BigQuery-style
names, which numeric_range_attributes() matches on. A repeated record's
DuckDB type already ends in `[]`, so the mode is not applied on top of it.
Specialized indices have no local Parquet until fetched, so they degrade
to the un-expanded RECORD rather than failing discovery.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds struct-column guidance beside the existing array guidance in both the
user guide and the idc://guide MCP resource (dot access for a field,
unnest first for a list of structs) with runnable examples.
Also moves the illustrative `v24` / `24.0.0` strings in the OpenAPI
examples, model and tool docstrings to v25, updates the DOI count in the
citations comments (237 -> 242), refreshes the stale pin example in the
deploy runbook, and records both user-visible changes in the changelog.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Addresses Copilot review on #56.
Pin idc-index==0.13.0 rather than >=0.13.0. The production image installs
from pyproject.toml alone — the Dockerfile copies pyproject.toml and src,
never uv.lock — so the lockfile constrains CI but not the image. Under a
lower bound, rebuilding this very commit after a later idc-index ships
would bake a different idc-index-data, and the service would advertise an
IDC release at /v3/version that neither this PR nor the changelog claims.
dev/deployment.md already prescribed the exact pin; the constraint now
matches, with a comment saying why this one dependency differs.
Also finish the DOI-count update: the v24 -> v25 edit caught the mentions
that named a version and left four bare "237"s reading inconsistently
beside "242 at v25". All are prose; no assertion depends on the number.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Opened #57 for the root cause behind the exact-pin discussion above.
The idc-index==0.13.0 pin in this PR fixes the one dependency where resolution drift is user-visible (it decides the IDC release advertised at /v3/version). The general problem is wider: Dockerfile:14-16 installs from pyproject.toml alone, so uv.lock constrains CI but not the image — meaning every other runtime dependency can still differ between the versions CI tested and audited and the versions that ship.
The new schema contract is asserted only through the core service. Repository guidance requires capability changes to retain core/REST/MCP parity; add an async parity assertion comparing this expanded schema with GET /v3/tables/analysis_results_index and the MCP get_table_schema result so adapter serialization regressions are covered.
Test does not verify missing-Parquet fallback behavior
tests/test_struct_columns.py:74
This test does not actually pin the missing-Parquet fallback: it verifies that seg_index lacks a path but then describes clinical_index, and the final assertion explicitly accepts STRUCT(...), which is the non-fallback result. Use the same table throughout, force that table's Parquet lookup to be absent (clearing the schema caches), and assert the exact bare RECORD/RECORD[] result so a regression cannot pass.
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
…review)
Addresses the second Copilot review pass on #56.
The missing-Parquet test was close to vacuous: it asserted on seg_index
then described clinical_index, and its final `or startswith("STRUCT(")`
accepted the non-fallback result, so it could not fail. Replaced with
three that can: one table throughout asserting the exact bare RECORD[],
direct _column_type assertions for the fallback rendering, and the real
degradation path forced on a table that otherwise expands (drop its
parquet_filepath, clear the lru_caches, assert RECORD). Verified by
mutation — rendering an empty struct as "STRUCT()" fails all three, and
would have passed the old one.
Also adds the table-schema parity test the repo's own invariant asks for:
test_parity.py covered version, counts, citations and clinical, but not
the payload this PR changes. It pins core == REST == MCP for both struct
tables, and that collections_index.sources keeps its embedded quoted
identifier (`"Access" VARCHAR`) through JSON on both surfaces.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both points from the second review pass are fixed in 3257d3a — they were fair.
The fallback test was close to vacuous. It asserted on seg_index, then described clinical_index, and its final or ... startswith("STRUCT(") accepted the non-fallback result, so nothing could make it fail. Replaced with three tests that can:
one table throughout, asserting the exact bare RECORD[]
direct _column_type assertions for the fallback rendering, including that an unrelated expansion is not borrowed for a column that has none
the real degradation path forced on a table that otherwise expands — drop analysis_results_index's parquet_filepath, clear the lru_caches, assert RECORD (with a fixture clearing the caches on both sides, so a fake value cannot outlive the monkeypatch)
Verified by mutation rather than by eye: rendering an empty struct as "STRUCT()" fails all three. The old test passed that mutation, which is precisely the criticism.
Parity was genuinely missing.test_parity.py covered version, counts, citations and clinical, but not table schemas — the payload this PR changes. Added test_table_schema_parity_including_struct_columns: core == REST == MCP for both struct tables, plus an assertion that collections_index.sources keeps its embedded quoted identifier ("Access" VARCHAR) through JSON on both surfaces. That last one is the concrete serialization risk an expanded struct introduces, and it was untested.
Suite is 106 passing with all indices, 95 + 10 skipped bundled-only.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Moves the served data release from v24 to v25 and fixes a schema-discovery gap that v25's new column exposed.
Serving IDC v25
idc-index0.12.5 → 0.13.0, pullingidc-index-data24.2.2 → 25.0.0. That release is the data bump plus CI chores — no upstream API change./v3/versionand the MCPget_idc_versionnow reportv25: 179 collections, 26 analysis results, 1,044,191 series, 99.9 TB, 242 DOIs (up from 237).Schema drift across all 17 tables is two columns:
analysis_results_indexgainsprovenance, andprior_versions_indexdropsgcs_bucket_1(unreferenced here).Struct columns were undiscoverable
v25's
provenanceis a struct, andget_table_schemadescribed it as the bare typeRECORD— which names no field a caller could select. The same was already true ofcollections_index.sources, aRECORD[]whose elements nest two further structs (license,provenance). WritingSELECT provenance.data_contributormeant guessing field names, which is precisely what this server tells callers not to do.The upstream schema JSON ships no
fieldsfor a RECORD, so those names exist nowhere but the Parquet footer.get_table_schemanow reads them from there and advertises the real type:Three constraints shaped the fix:
RECORDcolumns are substituted. The rest keep their BigQuery-style names, whichnumeric_range_attributes()matches on.[], so themodeis not applied on top of it (no[][]).RECORDrather than failing discovery.The runtime path needed no change — structs already serialized correctly, and dot access and
UNNESTalready worked.provenanceis deliberately left reachable via SQL only; it is not promoted into the typedAnalysisResult/CollectionDetailresponses.Testing
tests/test_struct_columns.pypin the expansion, the no-double-[]rule, selectability, serialization, and the missing-Parquet fallback.ruff check/ruff formatclean;banditshows only pre-existingnosecnotes.Docs
Struct guidance sits beside the existing array guidance in
docs/user-guide.mdand is mirrored into theidc://guideMCP resource.INSTRUCTIONSis untouched — it stays lean. Changelog entry added under[Unreleased].The version bump to
3.0.0b5is not in this PR: the runbook requires it be its own commit, cut after this merges.🤖 Generated with Claude Code