Changes in version 0.4.0 Batch collection - register_source() takes an optional batch function that answers many requests in one upstream call. - collect_points() groups requests by source and variable list and hands a batch-capable source all of its requests at once. Unknown-source, missing-package and out-of-extent requests are refused as before. - collect_points() returns a final call column holding the upstream call recorded in each series' provenance. - nert collects many points and dates in one nert::collect_tern_data() call. options(terroir.nert_by = "10 days") sets the date step and options(terroir.nert_args = list(...)) passes further arguments. New sources - worldclim returns WorldClim 2.1 bio_1 to bio_19 through geodata. The licence (free for academic and non-commercial use) is recorded in each series. - elevation returns SRTM 90 m elevation and slope in degrees through geodata and terra. The licence is non-commercial and requires attribution to CIAT. - soilgrids reads SoilGrids 2.0 means at nine properties and four depths from 0 to 60 cm from the cloud raster (CC BY 4.0). - chirps returns CHIRPS 2.0 daily rainfall through chirps::get_chirps() (public domain). - options(terroir.cache_dir = ) sets where geodata downloads are kept. The default is tools::R_user_dir("terroir", "cache"). Minor improvements and fixes - silo fills the unit column and records the real weatherOz call. - nert keeps ASC_EV order codes as numbers with unit "ASC order code", drops the pixel_qa flag columns and fills units from the TERN product records. The AET unit stays empty because TERN's record says mm per month while the values look like mm per day. - options(terroir.nert_grid = ) puts every nert request in a batch on one shared date grid, so a batch reads each raster date once. - nert refuses PHENOLOGY, whose EVI fields are integers scaled by 10,000 and would fail the NDVI/EVI range check. - terroir_external_facts() records the worldclim, elevation, soilgrids and chirps licences and the silo units, each read from its authority page on 2026-10-06 with the page URL in evidence_path. - terroir_external_facts() marks an unchecked fact "ungrounded" in the grounding column instead of "[unverified]"; terroir_fact_status() counts both values. - The vignette was revised for clarity and completeness. - The minimum R version is now 4.1.0. The imports need only 3.4, but testthat and most adapter upstreams require 4.1, so an older floor could not be tested. A global soil source - New source isric: ISRIC SoilGrids point profiles, global and keyless. Texture, organic carbon, pH, bulk density, CEC, nitrogen and the water limits LL15/DUL/SAT, one value per property per depth layer, retrieved through apsimx. This closes the gap a running study had already routed around: studies/A0/01_generate_belt.R reached past terroir into apsimx for soil and left the uplift note behind, because a grain decision needs soil as much as it needs weather. - It does not intersect nert. nert serves TERN soil, but over Australia only and only with a key. isric is global and keyless, so inside Australia a caller may prefer TERN's measured products and everywhere else this is the only soil terroir has. - Soil is dateless by design. The date column is NA for a soil series rather than stamped with the retrieval time: a time-invariant property carrying a date invites a consumer to difference two retrievals as though something had changed. - Soil plausibility bounds join the value conformer. Texture, pH, bulk density, carbon, CEC, nitrogen and the water limits are range-checked at the contract. SoilGrids' own service returns integers scaled by ten and apsimx unscales them, so the scale error is one upstream change away rather than impossible, and it is the same class the NDVI bound already guards. - The statistic is selectable, within a stated limit. mean or Q0.5. ISRIC publishes Q0.05 and Q0.95 as well, which is what would let soil enter an analysis as a prior rather than a point value, but the upstream wrapper does not expose them; asking for one refuses typed rather than quietly returning the mean under a quantile's name. Changes in version 0.3.0 - New source: ocular / Microsoft Planetary Computer (source = "ocular"). Sentinel-2 and Landsat spectral-index series (NDVI by default) over a 500 m square window centred on the point, retrieved through the ocular package (AAGI-AUS) from the Planetary Computer SpatioTemporal Asset Catalog. No key. variables[1] selects the index, variables[2] the sensor ("auto", "sentinel-2", "landsat-8", "landsat-5"). ocular is an upstream, not an ORCHESTRA member: it sits in Suggests, is reached only through requireNamespace(), is commit-pinned in Remotes:, and every error or warning it raises is converted into a terroir_refusal -- no upstream condition reaches the caller untyped. The adapter registers a [-180, -80] x [180, 84] extent, because ocular builds its window in UTM and is undefined outside that latitude band. - Pixel cloud masking is on by default, and is terroir's own. The series is masked against the Sentinel-2 Scene Classification Layer (classes 0, 1, 3, 8, 9, 10, 11) or the Landsat Collection-2 QA_PIXEL bits (0-5); scene-level cloud filtering alone leaves cloudy pixels in the window and collapses the index on overcast dates. The mask is applied to the returned index rasters rather than through the upstream's own scl_classes argument, because at the pinned commit that argument silently drops every scene (its band list reaches do.call() named, so the mask branch operates on a plain list and errors into the per-scene handler). A date whose window is entirely masked is dropped from the series rather than returned as a spurious near-zero index. - Vendored upstream patch for ocular's tile merge. ocular:::.mergeRasterTiles() splices a named raster list into do.call(), so every multi-tile date fails with argument "x" is missing. terroir installs a behaviourally identical replacement that unnames the list, into ocular's namespace, on first use of the adapter (never at package load) and once per session. It is declared in the returned provenance and is removed when the fix lands upstream. - Retrieval provenance on an ocular series. Beyond the contract's five provenance fields, an ocular series carries the STAC endpoint and collection id, the resolved sensor, the index name, the window size, the scene-level cloud ceiling, the mask asset / kind / class set, the scene dates, the scene ids and the ocular commit actually installed (read from ocular's own DESCRIPTION, so a drifted install is visible rather than assumed). Scene ids are read verbatim out of the returned asset URLs -- ocular's scene records do not retain the STAC item id -- and are NA when no identifier is present; the signed URLs themselves are never recorded. - Unmaskable scene refuses. A scene whose STAC item carries no SCL / QA_PIXEL asset cannot be cloud-masked; the adapter refuses (naming the scene date and the missing asset) rather than returning it unmasked inside a series advertised as cloud-masked. - Two external-fact families grounded. ocular.stac_collection_id (sentinel-2-l2a, landsat-c2-l2) and ocular.stac_endpoint were resolved against the Planetary Computer STAC catalogue on 2026-09-02 -- each collection endpoint returned its own id -- and carry a real verified_on. The adapter additionally re-checks the asserted collection id against the one ocular configures for the resolved sensor at call time, and refuses on a mismatch rather than mislabelling the provenance. - collect_points(): batch/panel collection over a list of requests. The fleet-audit-named batch verb (T-11): dispatches each request through [collect_point()] and stacks the results into one data.table, tagged with a request_index and a status ("ok"/"refused") column. A refusal is kept as a row (with its reason recorded), never dropped from the stack, so a multi-site collection still accounts for every request it was given. - Refusal, not abort, on an implausible value. collect_point()'s value-plausibility conformer (out-of-range NDVI/EVI or temperature) now returns a structured terroir_refusal instead of aborting the call, so one bad cell no longer stops a whole collection. verify_geo_series() called directly is unchanged and still aborts. - Removed the inert radius formal from geo_point_request(). It was documented, validated and stored, but read by no adapter -- no registered source implements a nearest-station or buffer search today. Reintroduce it (on the SILO/GSOD nearest-station path) if/when such a source is added. Breaking for any caller passing radius = . - Continuous integration. Added R-CMD-check (macOS / Windows / Ubuntu, release/devel/oldrel-1) -- the fixture-replay tests need no network and no keys, so the whole suite runs clean-room on every push/PR to main. - New geo_point_schema(). Exports the fixed ten-column C6 response schema, so a sibling package can read the contract's column order instead of reaching into the internal terroir:::.geo_point_cols. - Documentation pass. The vignette now gains a small-multiple figure of a replayed series (temperature and precipitation panels, captioned and interpreted), lists nert in the sources table, drops the stale "flexyBayes constellation" framing (now "the ORCHESTRA", also fixed in DESCRIPTION and README.md), and states plainly that unit is not yet populated for the nasapower/silo/abares adapters. README rewritten with an install section, a runnable offline example, and a corrected Status/Sources section (was stale at v0.0.0.9000). collect_point(), verify_geo_series() and register_source() gained @examples. Changes in version 0.2.0 - New source: nert / TERN (source = "nert"). Point-extracts TERN's Australian soil, soil-moisture (SMIPS), actual-evapotranspiration, canopy-height and phenology layers via the nert package (AAGI-AUS), behind the standard request/response contract (free TERN API key; Australia extent; refuses keyless or out-of-coverage). Independent-Oracle discipline: nert is the flagship co-hallucinated-oracle case, so terroir consumes the reviewed nert 1.1.0 and does not trust it blindly -- only finite COG values are emitted (an all-NA return is refused, never blanked; a character-class layer such as ASC soil-order is dropped, not coerced), and the TERN dataset-id facts terroir asserts are registered in terroir_external_facts() as not yet checked (their oracle is the TERN catalogue directly -- not nert's own grounding -- and has not yet been diffed in a recorded session). nert is a Suggests (+ Remotes: AAGI-AUS/nert). Changes in version 0.1.0 - User-facing offline / fixture-replay mode. collect_point(req, offline = TRUE) (or options(terroir.offline = TRUE)) answers a request from a recorded fixture with zero network egress -- the sovereign-coordinate and reproducible-pipeline use cases. The fixtures (one per CORE source) ship in inst/fixtures/ with a manifest keyed by (source, lon, lat, date-range, variables); a matched series is replayed with provenance marked replayed = TRUE and the recording's original retrieved_at preserved (is_replayed()). When no fixture answers a request, offline mode returns a structured refusal rather than silently reaching the network. Offline mode needs no Suggests package installed. A custom fixture directory is set via options(terroir.fixture_dir = ). - Coarsened-coordinate (sovereign-GPS) mode. geo_point_request() gains sovereign and coarsen. A point flagged sovereign = TRUE is coarsened to roughly a one-kilometre cell (precision 0.01 degrees) before any upstream call, so the exact GPS never leaves the host; coarsen also takes an explicit precision (a single number, degrees) or a grid snap (list(grid = )) for a non-sovereign point. The request and the returned series' provenance record both the raw-requested and the coarsened-sent coordinates plus the policy. The privacy primitive is additive to C6: the fixed ten-column series schema is unchanged, so existing consumers are unaffected. - Live-grounded external facts (Independent Oracle Principle). The MODIS (product MOD13Q1, band 250m_16_days_NDVI, NDVI scale 0.0001) and NASA POWER (T2M, PRECTOTCORR) fact families were diffed against their authorities' own catalogues -- the live ORNL DAAC MODISTools::mt_products() / mt_bands() records and the NASA POWER parameter dictionary -- and carry a real verified_on (2026-06-13) in terroir_external_facts(). A new nasapower_variable_code family is enumerated. The SILO, ABARES and Sentinel-2 STAC families stay unchecked (no fabricated date): their authoritative oracle has not yet been run in a recorded session. The registry validator now reports grounding per family rather than all-or-nothing, so a grounded family passes while an un-grounded one fails with the un-grounded reason. - Object system: S3 is the deliberate choice. As a C6 producer (a classed data.table plus attribute-carried provenance and an optional-manifest pattern, the masque shape), terroir stays S3. An S7 migration is out of scope -- it buys nothing for a producer of a tabular contract. The inference-hub members own the S7 manifest contract; a collector does not need to. - Release ceremony: a hand-crafted inst/CITATION; a pkgdown site (_pkgdown.yml); the Collecting geo-point data with terroir vignette gains offline-replay, sovereign-coordinate and live-collect sections; the C6 contract status is lifted from draft to stable for the C6 machinery and the live-verified adapters. Changes in version 0.0.0.9000 - Independent Oracle Principle (Phase-1 grounding). External-authority facts are no longer grounded only against package-generated goldens. - terroir_external_facts() / terroir_fact_status(): a provenance registry enumerating every asserted fact (MODIS product/band/scale, SILO variable codes, ABARES AGFD layer names, Sentinel-2 band mapping) with the oracle that grounds it and a verified_on date; seeds ship all unchecked. - test-catalogue-diff.R: independent-oracle tests that diff the asserted codes against each authority's own catalogue (MODISTools::mt_products() / mt_bands(), weatherOz SILO values, the AGFD data dictionary). - The MODIS adapter now refuses when the authoritative NDVI scale is absent rather than defaulting to 1 (a silent 10,000x error). - verify_geo_series() gained a value-plausibility conformer (NDVI in [-1, 1], physical temperature bounds), and register_source() / collect_point() gained a per-source spatial extent so an Australia-only source is refused when queried outside its coverage. - Initial scaffold (constellation Phase 2). The data-collector member of the flexyBayes constellation. - Contract C6 geo_point_series: geo_point_request(), collect_point(), new_geo_point_series(), verify_geo_series(), and the free-only source registry (register_source(), list_sources()). - CORE adapters: nasapower, silo (weatherOz/SILO), abares (read.abares), modis (MODISTools), stac (rsi / Sentinel-2). nasapower is verified end-to-end; the network/key adapters ship with golden-file tests and a data-raw/regenerate_golden.R regeneration script. - Structured refusals (is_refusal()) for unknown sources and missing source packages -- terroir builds and checks with no source package installed.