0008 — Bundles are the public record: immutability, archives, version lines and download metrics¶
- Status: Proposed
- Date: 2026-10-18
- Plan:
docs/superpowers/plans/2026-10-18-bundle-record-and-metrics.md - Amends: ADR 0004 §3 (no live window: a superseded line freezes at the switch-over and is kept indefinitely, with no EOL date) and §5 (the in-place exception ends with 1.1.0); ADR 0006 (on PR #146) consequences (advance notice, (g)).
- Amended: 2026-10-20 by ADR 0009 (R2 layout mirrors NCBI ClinVar):
(a) weekly fulls are an exception to immutability; (c) archives are
archive/YYYY/inside a line, and the 1.0.1 generation becomes the frozen linev1/1.0/; (e) one overwritten Parquet dataset, none in archives. - Decided: 2026-10-21 (maintainer): 1.1.0 is GA (a); superseded lines are frozen and kept
indefinitely, status
frozen, with noeol, nodeprecatedstatus and no deletion window (b); a content-only correction that rewrites bundles opens a new MINOR line (c, ADR 0009 §5). Still open: N in (f). - Amended: 2026-10-21 to match the code merged on
1.0(PR #182,r2_layout.py): the finished month's newest weekly full is kept until the new month's monthly is published (a); the switch-over notice usesstatus: "frozen"andsuperseded_byas the root and lineindex.jsonpublish them (b); the Parquet manifest fields (e).
Context¶
Every corrective release so far has rewritten the published files under /v1/ in place: the 1.0.0
re-release, 1.0.1, and next the 1.1.0 republish (ADR 0006, on #146). Each rewrite replaced the bytes behind
the same URLs. Anyone who downloaded an earlier generation now holds files that no longer exist anywhere, and
nothing tells them which generation they have.
Before deciding how much machinery that problem deserves, we need to know whether anyone is downloading the data. What we know today:
- Analytics exist only from 2026-10-14. Cloudflare GraphQL Analytics (
httpRequestsAdaptiveGroups, per path, Free plan) coversdata.clinvar-gkm.orgfrom the day it started serving (ADR 0005). The zone rejects queries older than "4w3d", so it keeps about 31 days. Earlier traffic went to ther2.devURL, which has no per-path analytics and is now disabled. -
No complete full-bundle download so far. From 2026-10-14 to 2026-10-18, excluding our own IP:
Path Requests Clients Served Reading v1/datasets/clinvar-gkm_00-latest.json.gz18 8 IP/UA pairs 1.09 GB about 0.2 of one 4.8 GB full: at most a partial download dated monthly fulls (each) 2–4 — 0 bytes HEAD requests or link checks v1/deltas/00-latest/…delta12 — 0.03 GB about 1–2 delta downloads old flat-root /datasets/…7 — 0 bytes stale links -
Parquet is not yet stable. The Parquet tables are re-derived from the BigQuery dict tables by separate SQL (ADR 0007, Workstream 1), and their layout changed in 1.0.1 and changes again in 1.1.0. The JSON bundles are the canonical output (ADR 0007, principle 2).
- The 1.0.1 bundles are being archived to
archive/1.0.1/(thev1/paths mirrored below it), with aMANIFEST.json(sha256 per file) and aREADME.txt, before the 1.1.0 republish rewrites/v1/. Under ADR 0009 this generation moves to the frozen line folderv1/1.0/. - No subscribers yet. The email list
clinvar-gkm@broadinstitute.orghas no members. When this ADR was proposed the schema had not been declared GA; the maintainer declared 1.1.0 GA on 2026-10-21 (a).
Decision¶
(a) The JSON bundles are the public record¶
The monthly full and delta JSON bundles are the published record of ClinVar-GKM. From the 1.1.0 republish
on, a published bundle file is immutable: the bytes behind a published bundle path are never replaced. A
correction is published into a new line folder (c), and the folder it replaces is frozen with its bytes intact.
The mutable pointers are not records: index.json, the root manifest, feed.xml and every 00-latest path
keep moving to the newest release.
Two amendments from ADR 0009:
- Weekly full bundles are not records. The current month's weekly fulls under
weekly/are a convenience copy and are deleted at month rollover, as NCBI deletes its weekly releases. The finished month's newest weekly full is kept until the new month's official monthly, a server-side copy of it, is published, and is deleted then. This is a deliberate exception to immutability. The records are the official monthlies and the deltas, from which any past week can be rebuilt exactly. - Moves change the path, not the bytes. At month and year rollover, deltas and monthlies move into
deltas/YYYY-MM/andarchive/YYYY/. Their bytes, names and sha256 stay the same.
1.1.0 is GA (maintainer decision, 2026-10-21). The ADR 0004 §5 initial-stabilization period, which ADR 0006
extended to cover the 1.1.0 reorganization, ends with 1.1.0: the in-place exception is closed, published
files are immutable from 1.1.0, and every later breaking change opens a new MAJOR line (v2/) under the
ADR 0004 MAJOR-line and RFC process.
(b) A breaking change opens a new line¶
A breaking schema change after 1.1.0 opens a new line, v2/2.0/ (ADR 0009 §1), built in full (every monthly full and every
delta in the window being republished) alongside /v1/. The old line is then frozen at the switch-over: no
new releases, and its files stay downloadable and unchanged. It carries a switch-over notice:
- in its
index.jsonand in its entry in the root manifest'sversions:"status": "frozen"and"superseded_by"naming the line that replaced it (for example"v2/2.0"), asv1/1.0already carries"superseded_by": "v1/1.1"; - in its
MANIFEST.jsonandREADME.txt(c); - on the email list and in GitHub Discussions Announcements.
Decided 2026-10-21 by the maintainer: a superseded line is frozen at the switch-over and kept
indefinitely. Its status is frozen; there is no eol date, no deprecated status and no deletion window.
This amends ADR 0004 §3: the prior line does not stay live for a 6-month window, and the root manifest carries
no EOL date. Only one line is current at a time; frozen lines accumulate.
(c) A replaced generation is kept as a frozen line folder¶
Amended by ADR 0009. Each version line has its own folder,
v{major}/{major}.{minor}/. A republish that replaces a line's bundles (a new MINOR or MAJOR) publishes into a
new folder, and the folder it replaces is frozen in place and kept indefinitely (b): nothing is added, moved
or deleted, and it is listed from the root manifest with its status. A frozen folder carries:
- a
MANIFEST.jsonlisting every file with its size and sha256; - a
README.txtsaying what the generation is, which release replaced it and why.
The 1.0.1 generation, first copied to archive/1.0.1/ on 2026-10-18, moves to v1/1.0/. There is no root
archive/<version>/ folder any more: archive/YYYY/ now means the prior-year archive inside a line
(v1/1.1/archive/YYYY/, monthlies plus deltas/YYYY-MM/). A content-only correction that must rewrite bundles
is published the same way, as a new MINOR line folder; a PATCH only adds new dated files (ADR 0009 §5,
decided 2026-10-21).
(d) Every manifest carries sha256 per file¶
Every manifest that lists a bundle (the per-line index.json, each delta manifest and each frozen line's
MANIFEST.json) gives its size and sha256, and every bundle has a <file>.sha256 sidecar. A user can then tell which generation a local copy belongs to by
hashing it, without relying on dates or file names.
(e) Parquet is derived, not a record¶
Parquet is a convenience view of a bundle, not a record:
- The docs and the manifests label it "derived from the bundle; may be regenerated".
- Amended by ADR 0009. One Parquet dataset is published per
line, at
v1/1.1/parquet/, rebuilt from the newest release and overwritten every release.parquet/manifest.jsonbeside it givesderived: true, thebuild_date, thesource_releaseanddataset, thebuilderandtable_spec_version(the tool version), theschema_version, and each file's size and sha256. There is no Parquet inarchive/YYYY/, in frozen line folders (v1/1.0/has none) or per delta. This reverses the 2026-10-18 decision to keep one Parquet set per archived generation. - A versioned Parquet rebuild tool,
clinvar-gkm-parquet(planned), builds the Parquet tables from a published bundle. It is versioned on its own cadence, declares which bundleschema_versions it accepts, and the pipeline pins a version of it (ADR 0007, Workstream 1), so a user can rebuild the Parquet of any monthly with the tool version that matches. - Caveat: the 1.0.x Parquet was produced by BigQuery SQL from the dict tables, not from the bundle, so it cannot be reproduced bit-for-bit. So is the 1.1.0 Parquet, since Workstream 1 starts after 1.1.0 ships. The tool can rebuild the content of those sets from their bundles, not their bytes.
(f) Download metrics decide how far to go¶
A daily collector stores per-path request counts, bytes served and distinct-client counts for
data.clinvar-gkm.org (no IP addresses). Estimated complete downloads of a bundle = bytes served ÷ its size.
Retention and the shape of a switch-over are no longer metrics questions: frozen lines and archive/YYYY/
are kept indefinitely, and a breaking change freezes the old line at the switch-over (b). The metrics decide
how far to go beyond that:
- Promote a frozen line or monthly if it shows N or more estimated complete downloads in a calendar month: consider a citable copy (for example a DOI) for it.
N is the maintainer's decision. The first review is 60 days after collection starts (plan, task 7).
(g) Announcements¶
An advance notice before a republish or a switch-over is required once the email list has subscribers or after GA, whichever comes first. With 1.1.0 GA, every later republish into a new line and every switch-over gets an advance notice. 1.1.0 itself used a completion notice only (changelog, GitHub Release notes, Discussions); this amends the advance-notice requirement in ADR 0006's consequences.
Consequences¶
- Storage. One frozen generation of bundles is about 24 GB (19 dated bundles, 19.3 GB, plus the
00-latestcopies, 4.8 GB): about $0.36 a month at R2's $0.015/GB-month. Frozen lines are kept indefinitely, so this grows by one generation per new line. Only one Parquet dataset exists under (e), so dropping the dated Parquet sets saves about 65 GB. Weekly fulls add at most about 24 GB at the end of a month (ADR 0009). Av2/2.0/line adds a full live line beside the frozenv1/lines. - Operations. Each republish that replaces a line gains steps: freeze the old line folder, write and
verify its
MANIFEST.json, write itsREADME.txt. Month and year rollovers move and delete files, and each move or delete is followed by a CDN purge of the old paths (ADR 0009). The uploaders andgenerate-r2-index.shpublish sha256,status/superseded_by, each monthly'ssource_releaseand the frozen lines in the root manifest (built inr2_layout.py, PR #182). A daily GitHub Actions job collects the download stats (secretsCF_ANALYTICS_TOKEN,CF_ZONE_ID); a missed day can be collected again only within the 31-day retention. - For users. A monthly or delta file keeps the same bytes and name, though its folder changes at month
and year rollover; a weekly full is gone shortly after its month, and is rebuilt from a monthly plus
deltas; a superseded generation stays available indefinitely as a frozen line folder (
v1/1.0/); a checksum tells them which generation they hold; a frozen line says what replaced it; and they can rebuild Parquet from any monthly withclinvar-gkm-parquet. - Metric limits. Cloudflare's adaptive sampling makes counts estimates. Distinct clients are counted per day by IP, so NAT undercounts and roaming clients overcount. Bytes ÷ size counts two half downloads as one. Traffic before 2026-10-14 is not measurable.
- Harder: from 1.1.0 (GA), a mistake in a published bundle can no longer be fixed quietly. It needs a new MINOR line (content-only, ADR 0009 §5) or a new MAJOR line (breaking), with an advance notice (g).