feat(rust)!: decompress and validate the prebuilt fullmap in Rust, matching a force build - #97
Merged
Merged
Conversation
…+tar, GIL-free) [US-001]
…se/multi-primary/zstd-magic hardening [US-002]
…the force-build contract [US-005]
…ract round-trip [audit]
|
Important Review available on request
Reviews should be triggered manually for repositories with fewer than 10 stars. Select Trigger review above or comment ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
SkyeAv
added a commit
that referenced
this pull request
Aug 14, 2026
Cut 12.0.0 and bump the package version in pyproject.toml, uv.lock, and CITATION.cff. Major, not minor: three breaking changes accumulated since 11.0.0 — the agent now builds into a caller-owned graph config instead of maintaining an internal registry (the graph_registry module, <state-dir>/graph.yaml, and the rebuild-agent-graph subcommand are gone), the QC embedding stage migrated from BioBERT to SapBERT with the model cache moving to .tablassert/sapbert/ (#93), and build-fullmap's prebuilt extraction moved into the Rust extension with force-build-contract validation before install (#97). The changelog needed restructuring, not just promoting: #93 and #97 appended their entries inside the 11.0.0 section, so the SapBERT, abbreviation-stage, and prebuilt-extraction entries are moved into 12.0.0 where they belong, and the caller-owned-graph-config change landed with no entry at all, so its breaking change (the removed --fullmap flag and rebuild-agent-graph subcommand, in-place graph mutation, orphaned registries, and the graph=/graph_path= API) is written up here. Also ships in this release: approval_ids as a curated pass-through edge field, build-kg --threads for parallel fullmap reads scaling past the 16 record shards (#101), release-mode dropping of zero effect-size edges (#95), the species_context_qualifier derivation fix, and the fix that stops fabricating a supporting study for sections with no publication and nothing to preserve (#99).
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
The prebuilt
fullmap.tar.zstthatbuild-fullmapdownloads is now decompressed, extracted, validated, and installed entirely in the Rust extension — streaming zstd→tar with the GIL released — and the installed bundle is checked against the exact contract a--forcebuild produces before anything is renamed into place.Changes
What was changed
Rust extraction — new
rs.extract_prebuilt_fullmap(archive, output, progress=None)inrust/src/fullmap.rsstreamsFile → 8 MiB BufReader → zstd::Decoder → tar::Archive(the multi-GB decompressed tar never touches disk), extracts into a dot-prefixed temp dir on the output's filesystem, validates the bundle, then atomically renames the primary →outputand shardi→<output-stem>.s<i>.redb. The whole extract→validate→rename runs insidepy.detach, mirroringbuild_fullmap_db; the optionalprogresscallback re-acquires the GIL briefly per member ("opening archive","extracting <entry>","validating"). Addszstd(libzstd, vendored) +tardeps.Force-build validation — before any rename, the extracted bundle must satisfy the same contract the read path enforces: the primary opens read-only, META
schemais exactlytablassert.fullmap.v5(older tags are rejected loudly, not warned),build_idis present, the shard set is exactlys0..s{shards-1}with no gaps or extras, and every shard'sbuild_idequals the primary's. It reuses the read path's own helpers (validate_schema,read_build_id,shard_count_of,open_read_only), so the contract cannot drift.Hardening — symlink/device entries, PAX
GNU.sparse.*records (tar-rs ignores them, which would silently corrupt a bsdtar-packed archive),..path traversal, multiple unnamed primaries, and non-zstd garbage (a 4-byte frame-magic pre-check;zstd::Decoder::newis lazy) are all rejected loudly. Every failure leaves nothing besideoutputand removes the temp dir.CLI wiring —
fetch_prebuilt_fullmapkeeps download + sha256 orchestration and delegates extraction to the new_extract_prebuilt_fullmapseam (RustRuntimeError→PrebuiltFullmapUnavailable, chained;KeyboardInterruptpasses through)._extract_zst_tar/_stream_tarand theirshutil/tarfile/tempfileimports are deleted. Fallback semantics are unchanged: any failure falls back to the from-scratch BABEL build, and the archive is kept on failure so the retry reuses the cached download.Why this change is needed
Extraction previously ran in Python — native
tarfilezstd on 3.14+ or azstdsubprocess pipe on older interpreters — holding the GIL for the multi-GB decode and adding a process boundary in the IO path. Moving it into the extension makes the decompression a single streaming Rust pipeline with the GIL released. And nothing verified that what RENCI published actually matched a local build: a torn or wrong-version archive would install silently and only fail later at lookup time. Validation now happens before install, so a bad prebuilt falls back to the build path instead of landing a broken database.How it affects the project
User-visible: faster extraction, progress rendering preserved, and an invalid prebuilt now surfaces as the existing "fall back to BABEL build" path instead of a silently broken DB. The
zstdsystem binary is no longer consulted at all — Python 3.11–3.13 no longer need it installed.Breaking change
Internals-only; no user-facing migration. The private
cli._extract_zst_tar/cli._stream_tarhelpers are gone (the monkeypatch seam is nowcli._extract_prebuilt_fullmap). Two environment deltas: building from source now needs a C compiler for the vendored libzstd (cc— already present in CI and any Rust toolchain), and the runtimezstdbinary requirement on Python < 3.14 is dropped, not added.Docs
docs/fullmap.mdanddocs/cli.md— the prebuilt path now describes the Rust-side streaming extraction and the validate-before-install contract (no staletarfile/zstd-binary wording remains).CHANGELOG.md—## Unreleased→### Changedentry covering the move, the validation contract, and the loud rejections.Testing
make check— exit 0: ruff + ruff format clean,pyright0 errors,uv run pytest883 passed, 35 skipped,cargo test69 + 10 + 14 passed (1 ignored),cargo clippy --all-targets -- -D warningsclean,cargo fmt --checkclean.cargo test --manifest-path rust/Cargo.toml --test extract_prebuilt— 14 passed: the positive round-trip extracts a real force-built fixture (custom stem, nested members) and pins the canonicalterm → CURIEsmap identical to the source DB, plus the progress-callback contract (opening archive→ per-memberextracting→validating); 12 negatives cover corrupt/garbage, missing primary, shard gap, extra shard, outdated schema (v1 rewrite via writable redb copy), non-redb primary, traversal, symlink, missing archive, PAX-sparse, duplicate/stray primaries — each asserting actionable error text, nothing landed besideoutput, and no temp-dir residue.uv run pytest tests/test_cover_cli.py -q— 49 passed, including the real end-to-end test: a force-built DB packed into a real.tar.zst, downloaded through a fakeddownload_babel_file, extracted by the real Rust path with zero extract monkeypatching, and pinned to answerrs.lookup_fullmap_termsidentically to the force-built DB.rust/tests/build_golden.rsstays byte-exact after its helpers moved torust/tests/common/mod.rs(shared with the new test): 10 passed includinggolden_output_is_pinned.Deferred
tar --sparse-packed fixture test for theGNUSparseexpansion branch, and negatives for duplicate shard indices across subdirectories and a.redb-suffixed directory — behavior is correct (verified live during audit) but unpinned.