Modernization: Phase 0 audit + Phase 1 correctness & dependency hygiene (0.4.0) - #34
Draft
gavinrozzi wants to merge 25 commits into
Draft
Modernization: Phase 0 audit + Phase 1 correctness & dependency hygiene (0.4.0)#34gavinrozzi wants to merge 25 commits into
gavinrozzi wants to merge 25 commits into
Conversation
Records the discovery-phase findings for the modernization effort: baseline R CMD check and test results, function-by-function dependency autopsy, upstream (uszipcode-project) reproducibility verdict, the verified identity transformation contract between the 0.2.6 simple_db snapshot and the shipped zip_code_db, missing-ZIP forensics for issues #19/#25/#26, and the recommended Phase 2 pipeline decision. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
The ordering bug reported in #20 was fixed in 0.3.4 but never gained a regression test. Locks in both reprexes from the issue thread (the README example and the repeated-pair case), NA behavior for coordinate-less ZIPs, and the units/lonlat arguments. Also wraps the pre-existing bare normalize_zip assertions in proper test_that() blocks for testthat edition 3. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
…#27) reverse_zipcode() previously returned rows in database order, collapsed duplicate inputs to a single row, and could therefore not be used inside dplyr::mutate() (the size-mismatch error reported in #27). Both functions now match inputs with match(), guaranteeing one output row per input element, in input order, with NA rows (and a warning) for ZIP codes not present in zip_code_db. geocode_zip() previously also silently dropped unmatched ZIPs; it still errors when no input matches at all. Tests written first to reproduce the exact reprex from the issue thread. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
, #33) Removes the raster dependency - and with it the retired sp lineage and the GDAL/GEOS/PROJ system-library chain loaded at attach time - by replacing the two raster::pointDistance() call sites with an internal vectorized haversine function (mean Earth radius 6,371,008.8 m). - zip_distance() now looks up coordinates via match() (no join), keeping strict input order; lonlat = FALSE retains its previous meaning (planar distance in degree units) for backward compatibility. - search_radius() computes all distances in one vectorized call instead of a per-row loop (~42k iterations), dropping runtime from seconds to ~10 ms; its intended NA-coordinate filter, previously a no-op due to argument shadowing, now actually runs. - Distances are now spherical (haversine) rather than WGS84 geodesic; results can differ from previous releases by up to ~0.5%. - Fixes tidyselect .data-in-select() deprecation warnings in the touched functions and a duplicated backticked %>% roxygen import. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
tidycensus (and its sf -> GDAL dependency chain, the source of the libarrow/GDAL load-time warnings) was imported wholesale solely to read the static tidycensus::fips_codes reference table in search_fips() and get_cd(). The 3,256-row Census FIPS table (public domain) is now bundled as internal package data in R/sysdata.rda, regenerable via data-raw/fips_codes.R with provenance documented. With raster and tidycensus both removed, library(zipcodeR) no longer touches GDAL, GEOS, PROJ, arrow, or any retired r-spatial package. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
normalize_zip()'s single tidyr::extract() call is replaced with base regmatches()/regexec(). jsonlite, httr, curl, RSQLite and DBI are used only by download_zip_data() and move from Imports to Suggests, gated by rlang::check_installed(). utils is now properly declared in Imports. Imports is now: rlang, stringr, dplyr, utils. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
…#14, #19, #32) - New FAQ vignette covering the western-hemisphere longitude sign convention (#14), missing/coordinate-less ZIP codes and the ZIP-vs-ZCTA distinction (#19, #26), jurisdiction accuracy caveats (#32), and the lazy-data installation error (#13). - zip_code_db docs gain a provenance-and-limitations section (#32) and fix the copy-pasted zipcode_type description; zip_to_cd docs disclose the pre-2020 redistricting vintage (#29). - geocode_zip docs note the longitude sign convention (#14). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
R-CMD-check now runs the standard v2 template: --as-cran checks across ubuntu-latest (release, devel, oldrel-1), macos-latest and windows-latest (release), replacing the retired ubuntu-20.04 images, v1 actions, and hand-rolled dependency caching. Adds a test-coverage workflow uploading to Codecov. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
…ry() Every issue addressed in this release cycle is referenced in NEWS.md. Adds AUDIT.md, CLAUDE.md and issue_comments/ to .Rbuildignore. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
Drafts for issues #13, #14, #20, #21, #24, #27, #28, #32, #33 — to be reviewed and posted manually, never posted automatically. Each header notes the suggested action (post/close). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
The pkgdown site permanently redirects from gavinrozzi.github.io to zipcoder.39n.io; R CMD check --as-cran flags the 301s. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
Six staged scripts (acquire -> build x3 -> validate -> finalize) rebuild every bundled dataset from pinned, SHA256-checksummed primary sources: Census 2020 relationship files, the 2024 Gazetteer, ACS 5-year estimates (vintage pinned, key via CENSUS_API_KEY), GeoNames place names (CC BY), and the MIT-licensed uszipcode snapshots as base + validation reference. The build strategy is carry-forward + refresh: the shipped database is the base (no ZIP is ever silently dropped), with upstream-1.0.1 rows, missing 2020 ZCTAs, and curated USPS-only ZIPs appended, and ZCTA-backed attributes refreshed from Census sources. A ~20-check validation gate hard-fails the pipeline before anything ships and emits a diff summary for release PRs. Licensing decisions (incl. the USPS-derived naming columns and the future HUD-USPS stage) documented in data-raw/README.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
Data releases are now versioned separately from code releases: metadata
(release tag, build date, source vintages, comprehensive-asset checksum)
is embedded in R/sysdata.rda by the pipeline and exposed via the new
zip_data_version() accessor.
download_comprehensive_data() fetches the ~450 MB comprehensive SQLite
database (full ACS profiles per ZIP) from the versioned GitHub data
release on demand - never automatically - verifies its SHA256, and
caches it in tools::R_user_dir("zipcodeR", "data") with a documented
offline path. Depends bumped to R >= 4.0 for R_user_dir.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
Validation gate: all checks passed (see data-raw/refresh_summary.md). - zip_code_db: 41,877 -> 42,725 rows; 0 removed. Every 2020 Census ZCTA now present (fixes #25 97003 and the territory ZCTAs behind #19), 787 Military ZIPs added (new zipcode_type value), curated USPS-only ZIPs incl. 91230 (#26). Coordinates/areas refreshed from the Census 2024 Gazetteer, demographics from ACS 5-year vintage 2023. - zcta_crosswalk: rebuilt on the 2020 ZCTA/tract vintage (168,212 rows). - zip_to_cd: rebuilt on the 119th-Congress relationship file (#29), following the method of PR #30 by @awallender. Tests updated where they pinned exact 2-decimal legacy coordinates, and extended with schema-contract and regression-ZIP checks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
refresh-data.yaml runs the data-raw pipeline (validation gate included)
on manual dispatch and quarterly, then opens a draft PR carrying the
pipeline's diff summary - a human always merges. Single lightweight job
with source caching; a commented runs-on line points it at the
self-hosted runner to save hosted minutes.
pkgdown.yaml had a pre-existing failure: an R snippet run without
'shell: Rscript {0}', which bash rejects. The step was redundant with
setup-r-dependencies and is removed (preferably added to extra-packages).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
…ema snapshots (#33) - search_radius() gains a cheap lat/lng window prefilter (5% inflated, pole-safe) before the exact haversine pass and now materializes only the three needed columns. bench/search_radius_bench.R records the numbers: pre-0.4.0 loop ~700-770ms / 4.3GB allocated per query; 0.4.0 boxed ~2.5ms / 3.5MB - roughly a 280x speedup - with result sets verified identical to the exhaustive pass. - Input validation with informative errors: search_radius coordinate/ radius checks, zip_distance units via match.arg and a length- compatibility check. - download_zip_data() is deprecated AND defanged to a no-op: its body could never refresh installed data, and when executed from a source checkout it overwrote pipeline-built files (observed during testing). - get_cd() drops its dead many-to-many join (fixes a dplyr warning) and resolves the state abbreviation via match(). - New test-04-schemas.R freezes every exported function's return schema as a testthat snapshot (forward-compat contract), plus validation and deprecation tests. Suite: 100 pass / 0 fail / 0 warnings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
Maps every 0.4.0 item to its issue, sizes the follow-ups (HUD crosswalk stage, FIPS enrichment for #7, comprehensive-DB functions, timezone refresh), and codifies the data-refresh, deprecation, and dependency policies. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
preferably is no longer on CRAN, so any::preferably cannot resolve; the site template installs from amirmasoudabdol/preferably instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
…d data versioning Correctness (search_radius prefilter): - longitude differences are wrapped onto [-180,180], so circles crossing the antimeridian keep their candidates (Adak/Aleutians/Guam) - the longitude window is sized at the highest-|lat| edge of the search circle instead of the query point, so large radii no longer drop higher-latitude candidates; near-pole/all-longitude circles skip the prefilter. New tests assert boxed == exhaustive on the exact cases the review found failing, plus the antimeridian reprex. Data (zip_to_cd rebuild): - Census ZZ not-in-any-district pseudo-rows excluded (31 rows dropped; gate now enforces 4-digit CD codes) - coverage restored for USPS-only ZIPs the pre-2020 HUD crosswalk had: district(s) derived from the ZIP's USPS city (6,410 ZIPs) or from the state where it has a single district (63); military ZIPs stay unmapped by design and get_cd() now warns on no-match instead of returning an empty result silently. Residual vs the old release: 1,266 USPS-only ZIPs with no principled derivation (documented). Gate gains a no-coverage-shrinkage check. Data versioning / download hardening: - comprehensive-asset tag+sha256 moved to an explicit pinned registry in sources.R (no longer derived from DATA_VERSION), gate-checked - data-raw/fips_codes.R re-saves every sysdata object instead of a hard-coded pair (no more zip_data_meta clobbering) - download_comprehensive_data(): Windows-safe checked rename; sha256 fallback passes -a 256 to any shasum variant (incl. shasum.exe), validates 64-hex output, and probes tools:: with inherits = FALSE Compatibility & data quality: - zip_distance() restores data.frame-style recycling for divisible lengths (undocumented break found by review) - state-modal timezone imputation for new ZIPs is now counted in the refresh summary; FAQ/CLAUDE.md/data docs updated to the CD119-derived zip_to_cd (54,817 rows) Suite: 111 pass / 0 fail / 0 warnings. R CMD check --as-cran: OK. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
User-facing: - zip_distance(lonlat = FALSE) had a unit bug in every prior release (Euclidean degrees put through the meters-to-miles conversion, returning 0.00 for essentially every pair). The mode is now deprecated with a warning and returns a planar equirectangular approximation in the requested units until removal. - get_cd() distinguishes ZIPs absent from zip_code_db (typos, numeric input that lost its leading zero) from known ZIPs with no district mapping, instead of misdiagnosing both as military/USPS-only; input is coerced to character first. - download_comprehensive_data() move-into-place simplified: rename, else overwrite-copy; the old cache is never deleted ahead of a successful replacement and a failed tmp cleanup is no longer treated as failure. - Dead NA-scrub line removed from the search_radius prefilter. Pipeline robustness (all rebuilt outputs verified byte-identical): - 02: half-specified coordinates (lat xor lng NA) are normalized to NA pairs, and the implausible-coordinate guard requires both coords non-NA, removing an any(NA) crash path; imputed_tz guards simplified. - 04: one shared eligibility predicate (!zipcode_type %in% 'Military' - fixing the NA-semantics drift between derivation and the unmapped count), one covered-ZIP intermediate for both lookups, named counts computed once for message/stats/summary, and tier disjointness asserted via stopifnot instead of a papering distinct(). - 05: refresh summary reports city-derived and single-district-state counts separately (the committed summary had mislabeled the merged count) and drops hardcoded 2010/pre-2020 baseline labels that would misdescribe future refreshes. - 06 + fips_codes.R: both sysdata writers use the load-env/re-save-all pattern with all.names = TRUE, so no internal object can be silently dropped by either script. - CLAUDE.md: lookup-function count corrected to 12. Suite: 114 pass / 0 fail / 0 warnings. R CMD check --as-cran: OK. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
Gate integrity (the big one): - 05_validate.R now takes its baseline from git (PIPELINE_BASELINE_REF, default HEAD) instead of the working-tree data/ files that 06_finalize.R overwrites - a rerun previously compared the candidate against itself, which is how the committed refresh summary showed '0 added' and how the CD-coverage change slipped past its own check. Rerun against master, the gate now sees the real diff: +848 ZIPs and exactly 1,266 lost CD mappings, matched against a new documented one-time acceptance constant (ACCEPTED_CD_COVERAGE_LOSS in sources.R, to be reset to 0 post-release per ROADMAP). The committed refresh_summary.md is regenerated against master and is now honest (848 added by type, 32,907 coordinate refreshes, 33,637 ACS refreshes, true previous row counts, no hardcoded vintage labels). - NEWS quantifies the 1,266 unmapped USPS-only ZIPs and the rationale (stale pre-2020 districts deliberately not carried; HUD stage will restore them), and corrects the dependency paragraph (httr removed entirely; Suggests serve download_comprehensive_data() and the pipeline, not the no-op download_zip_data()). User-facing hardening: - get_cd() rejects vector input with an informative error instead of silently recycling against zip_to_cd (or crashing in the warning branch). - search_fips(): the audit-listed nchar(county_fips < 3) misplaced parenthesis is fixed; >3-digit codes and unknown counties now error informatively instead of failing silently. - zip_distance(lonlat = FALSE) wraps the longitude difference onto [-180, 180], fixing ~5.5x errors on trans-dateline pairs. - download_comprehensive_data(): SHA256 capability verified BEFORE the ~450 MB download (no more download-then-discard loops), the download runs under a temporarily raised timeout (default 60s is ~13x too short), and a failed download explains that the pinned data release may not be published yet. tools declared in Imports. Suite: 121 pass / 0 fail / 0 warnings. R CMD check --as-cran: OK. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
Data semantics & API: - Document the at-large district-code flip: DC/PR/territories now carry the Census delegate code 98 (voting at-large states keep 00); search_cd() accepts "00" and "98" as aliases so pre-2020-style calls keep working, and NEWS + ?zip_to_cd explain the convention. - get_cd() labels every district with its own state: state_fips is now parallel to district, fixing silently wrong labels for the 258 ZIPs whose districts span a state line (02861 reported RI-01 as "MA"). - reverse_zipcode(NA) no longer crashes - a single NA yields an NA row with a warning, matching vector behavior. - normalize_zip(100000) boundary fixed (>= instead of >); numeric and character branches now agree. - search_fips(): unknown state FIPS errors informatively (previously a silent empty result while unknown county errored) and the state-only branch returns a tibble like every other search function. Pipeline & automation: - DATA_VERSION derives from the build date (override via PIPELINE_DATA_VERSION), so scheduled refreshes stamp new versions instead of reusing 2026.08 forever. - The CD coverage-loss acceptance is keyed to the exact 1,266-ZIP list (data-raw/accepted_cd_coverage_loss.txt); losing any OTHER ZIP fails the gate, so the acceptance cannot mask future regressions. ROADMAP says to empty the file post-release. - GeoNames US.zip is declared a floating source: its near-daily in-place regeneration no longer aborts scheduled refreshes; the downloaded hash is recorded for provenance instead of enforced. Docs: - README re-knit against the refreshed data (distances, 2020-vintage tracts); the staged issue-20 comment quotes current outputs and explains the coordinate-refresh delta. Not fixed (reviewer claim refuted): DESCRIPTION's Config/roxygen2/version 8.1.0 and the NAMESPACE format are exactly what the installed roxygen2 8.1.0 emits via devtools::document(). Suite: 136 pass / 0 fail / 0 warnings. R CMD check --as-cran: OK. Validation gate passes under both HEAD and master baselines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
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.
Modernization: Phase 0 audit + Phase 1 correctness & dependency hygiene
This draft PR is the first tranche of the phased modernization effort. It contains the
Phase 0 discovery audit (
AUDIT.md) and the Phase 1 "quick wins" — correctness fixes anddependency hygiene — targeting a 0.4.0 release. The reproducible data pipeline (Phase 2)
and performance/stability work (Phase 3) will follow on this branch or successors.
Phase 0 — audit (see
AUDIT.mdfor the full document)zip_code_dbproven byte-identical toSELECT * FROM simple_zipcodeofupstream uszipcode's
0.2.6-db-filerelease (2021-06-08) — the compatibility contractis an identity transform.
reconstruct the pipeline in R, validated against the upstream snapshots.
91230 is a USPS-only non-ZCTA ZIP absent from all upstream snapshots).
Phase 1 — what's in this PR
Correctness (#20, #27)
reverse_zipcode()/geocode_zip(): one row per input element, input order,duplicates preserved, NA rows for misses —
mutate()-safe. Regression tests built fromthe exact reprex in weird behavior when using reverse_zipcode inside a mutate #27.
zip_distance()ordering fix that shipped in 0.3.4.Dependency hygiene (#21, #24, #28)
rasterremoved (internal vectorized haversine replacespointDistance).tidycensusremoved (Census FIPS table vendored as internal data,data-raw/fips_codes.R).tidyrremoved;jsonlite/httr/curl/RSQLite/DBIdemoted to Suggests gated byrlang::check_installed().dplyr, rlang, stringr, utils— zero GDAL/GEOS/PROJ/arrow/retiredpackages at load. Package load: ~2.7 s / 54 namespaces → ~0.15 s / 25 namespaces.
Performance (partial #33)
search_radius(): vectorized distance pass (was a ~42k-iteration loop); typical querynow ~10 ms. Bounding-box prefilter + benchmark script planned for Phase 3.
Docs (#13, #14, #19, #29, #32)
caveats, lazy-data install error).
zip_code_dbprovenance-and-limitations section;zip_to_cdvintage disclosure.Infrastructure
Codecov coverage workflow;
citEntry()→bibentry(); version 0.4.0; NEWS.md withevery issue number.
Staged, not posted
issue_comments/holds draft replies for zip_code_db not found #13, Negative values for Longitude #14, zip_distance with vectors not returning correct order of results #20, Error: package or namespace load failed for ‘zipcodeR’ #21, libarrow.so.800 error #24, weird behavior when using reverse_zipcode inside a mutate #27, legacy package warning #28, Is there a way to get more accurate jurisdiction data? #32, Improve search_radius speed #33for maintainer review — nothing has been posted to any issue.
Verification
devtools::test(): 77 pass / 0 fail / 0 warnings (baseline was 51 pass + 28deprecation warnings).
R CMD check --as-cran: no errors/warnings/notes attributable to the package on thelocal run (details in AUDIT.md §1 and the CI matrix on this PR).
🤖 Generated with Claude Code
https://claude.ai/code/session_01WEHekDvK7kMM6rVT1vDsxg
Phase 2 — reproducible data pipeline & refreshed data (added)
data-raw/pipeline: pinned + SHA256-checksummed sources (Census 2020 relationshipfiles, 2024 Gazetteer, ACS 5-yr vintage 2023 via
CENSUS_API_KEY, GeoNames CC-BY,MIT uszipcode snapshots), carry-forward + refresh build, ~20-check validation
gate (all passing), licensing decisions in
data-raw/README.md.zip_code_db41,877 → 42,725 (every 2020 ZCTA incl. 97003 Zipcode 97003 and a dozen or so other zipcodes in Oregon are missing from zip_code_db #25;91230 No data found for ZIP code 91230 #26 via curated supplement; 787 Military ZIPs; 0 removed); Gazetteer
coordinates + ACS 2023 attributes;
zcta_crosswalk→ 2020 vintage;zip_to_cd→ 119th Congress (Outdated congressional districts in "data/zip_to_cd.rda" #29, crediting PR Update zip code/congressional district relationships (fixes #29) #30).zip_data_version()accessor;download_comprehensive_data()(checksum-verified,R_user_dircache, never auto-downloads; Depends → R ≥ 4.0). Thedata-2026.08release is uploaded as a draft — publish it to activate the function.
refresh-dataworkflow: manual dispatch + quarterly, opens a draft PR with the gate'sdiff summary; needs the
CENSUS_API_KEYrepo secret; one-line switch to theself-hosted runner.
shell: Rscript).Phase 3 — performance & stability (added)
search_radius(): bounding-box prefilter + 3-column materialization.bench/:~700–770 ms / 4.3 GB per query (0.3.5 loop) → ~2.5 ms / 3.5 MB (~280×), result
sets verified identical.
search_radius,zip_distance).download_zip_data()deprecated and defanged to a no-op — its body could neverrefresh installed data and, when run from a source checkout, silently overwrote
pipeline-built files (observed live during testing). Flagging for explicit maintainer
sign-off since it changes runtime behavior of an exported function.
get_cd()dead-code/joinfix. Suite: 100 pass / 0 fail / 0 warnings.
Phase 4
ROADMAP.md: release plan (0.4.0 checklist, sized 0.4.x/0.5.0/0.6.0 items incl. Add fips codes toreverse_zipcode()output? #7),and standing data-refresh / deprecation / dependency policies.
Final
R CMD check --as-cran: Status: OK (zero notes).