diff --git a/README.md b/README.md index a54c770..959a16d 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ `intent → task semantics → catalog federation → live item evidence → asset plan → manifest` [![CI](https://github.com/GeoGeekLab/stac-scout/actions/workflows/ci.yml/badge.svg)](https://github.com/GeoGeekLab/stac-scout/actions/workflows/ci.yml) -[![Python](https://img.shields.io/badge/Python-3.12%20%7C%203.13-3776AB?style=flat-square&logo=python&logoColor=white)](.github/workflows/ci.yml) +[![Python](https://img.shields.io/badge/Python-3.12%20%7C%203.13%20%7C%203.14-3776AB?style=flat-square&logo=python&logoColor=white)](.github/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/license-MIT-2ea44f?style=flat-square)](LICENSE) [![STAC](https://img.shields.io/badge/STAC-evidence--first-111111?style=flat-square)](https://stacspec.org/) @@ -129,17 +129,50 @@ confidence ≠ proof ## Install ```bash -python -m pip install -e ".[dev]" +python -m pip install stac-scout ``` -Python 3.12 or newer is required. +Python 3.12 or newer is required. Python 3.12, 3.13, and 3.14 are tested in CI. For Planetary Computer access recipes: ```bash -python -m pip install -e ".[planetary-computer]" +python -m pip install "stac-scout[planetary-computer]" +``` + +## 60-second first success + +This first check is deterministic and does not call a remote catalog: + +```bash +cat > request.json <<'JSON' +{ + "task": "assess wildfire impact", + "place": "Lahaina, Maui", + "datetime": { + "start": "2023-08-09T00:00:00Z", + "end": "2023-08-20T23:59:59Z" + } +} +JSON + +stac-scout advise request.json --task wildfire_impact ``` +The important part of the output is the decision contract, not prose: + +```json +{ + "data_type": "optical", + "required_measurements": ["nir", "swir22"], + "follow_up_requirements": ["comparison_windows"] +} +``` + +Scout derives the optical/NIR/SWIR2 requirement but refuses to invent the missing comparison +window. For a live Item-to-manifest walkthrough, see +[`examples/wildfire-impact/`](examples/wildfire-impact/). + ## Geo Task Intelligence ![Geo Task Intelligence workflow](docs/assets/geo-task-intelligence.svg) @@ -501,8 +534,11 @@ stac-scout/ │ ├── cases/ │ ├── federation_cases/ │ ├── task_cases/ +│ ├── regression_matrix.json │ ├── runner.py │ └── live.py +├── examples/ +│ └── wildfire-impact/ ├── docs/ │ ├── ARCHITECTURE.md │ └── TASK_RULES.md @@ -519,14 +555,14 @@ The deterministic evaluation corpus checks contracts that affect dataset decisio python evals/runner.py ``` -It covers: +It covers request invariants, modality mismatches, cloud PASS/FAIL/UNKNOWN, required versus +preferred measurements, DOI identity strength, multiple extents, antimeridian/polar AOIs, +empty and >100 Item behavior, provider timeout/429/partial failure, categorical resampling, +missing `file:size`, volume budgets, task/user conflicts, malformed provider metadata, +cross-provider identity, task derivation, temporal strategy, and follow-up requirements. -- request invariants; -- cross-provider identity; -- task-derived measurements; -- task modality; -- temporal strategy; -- follow-up requirements. +`evals/regression_matrix.json` binds the hardening acceptance scenarios to concrete pytest node +IDs, so removing a required regression causes the eval runner to fail. Remote providers are intentionally kept out of ordinary CI. @@ -548,7 +584,10 @@ remote catalog state ## Development +Contributor installs are editable; normal users should use the PyPI command above. + ```bash +python -m pip install -e ".[dev]" ruff check . ruff format --check . mypy @@ -557,7 +596,7 @@ python evals/runner.py python -m build ``` -CI runs on Python 3.12 and 3.13. +CI runs on Python 3.12, 3.13, and 3.14. Python 3.15 prereleases are exercised as a non-blocking compatibility signal before the final 3.15 release. Coverage must stay at or above 90%. diff --git a/examples/wildfire-impact/README.md b/examples/wildfire-impact/README.md new file mode 100644 index 0000000..55aee8a --- /dev/null +++ b/examples/wildfire-impact/README.md @@ -0,0 +1,98 @@ +# Wildfire impact: live evidence to replay + +This example exercises the complete STAC Scout decision path against a real STAC API. Remote +catalog contents can change, so exact Item counts are observations rather than golden test values. +The deterministic contracts behind the workflow are covered separately by CI and `evals/`. + +The AOI is a small polygon around Lahaina, Maui, and the Item window begins after the August 2023 +fire. A scientifically complete before/after impact study still requires an explicit comparison +window; STAC Scout intentionally does not invent it. + +## 1. Confirm task requirements + +```bash +stac-scout advise examples/wildfire-impact/request.json --task wildfire_impact +``` + +Expected semantics: + +```text +data_type: optical +required measurements: nir + swir22 +temporal strategy: before_after +follow-up: comparison_windows +``` + +The checked-in request already carries the required modality and measurements so the remaining +live steps use the same explicit hard constraints. + +## 2. Federate candidate discovery + +```bash +stac-scout federate examples/wildfire-impact/request.json \ + --max-workers 4 --overall-timeout 30 +``` + +Inspect `candidates[]`, `duplicate_groups[]`, and typed `failures[]`. A provider failure does +not erase candidates from other providers. + +## 3. Verify actual Items + +For a stable concrete path, use Element 84 Earth Search and Sentinel-2 L2A: + +```bash +stac-scout verify examples/wildfire-impact/request.json \ + --provider earth-search \ + --collection sentinel-2-l2a \ + --max-items 20 +``` + +The result records observed Item IDs, timestamps, asset keys, AOI coverage, and warnings. If the +provider returns exactly the cap, the observation is `limit_reached` rather than falsely called +complete. + +## 4. Produce the asset plan, volume evidence, manifest, and recipe + +```bash +stac-scout plan examples/wildfire-impact/request.json \ + --provider earth-search \ + --collection sentinel-2-l2a \ + --max-items 20 \ + --manifest scout.manifest.json \ + --recipe load.py +``` + +Inspect: + +- `access_plan.asset_choices[]` for measurement-to-asset evidence; +- each resampling decision and its evidence; +- `estimated_bytes` when `file:size` metadata is available, otherwise explicit UNKNOWN; +- output grid `utm` at 20 m; +- search completeness and selected Item IDs in `scout.manifest.json`. + +The recipe is generated from the manifest; the manifest is the provenance record. + +## 5. Replay the observation + +```bash +stac-scout replay scout.manifest.json +``` + +Replay reports complete/partial/inconclusive comparison status plus confirmed and unresolved +missing/new Item IDs. A capped historical or current search is never promoted into false +confirmed drift. + +## What this demo proves + +```text +task semantics + → cross-provider candidates + → live Item evidence + → asset selection evidence + → resampling evidence + → volume estimate or UNKNOWN + → versioned manifest + → completeness-aware replay +``` + +It does not claim that one post-fire window alone is a complete wildfire impact analysis. diff --git a/examples/wildfire-impact/request.json b/examples/wildfire-impact/request.json new file mode 100644 index 0000000..99970d9 --- /dev/null +++ b/examples/wildfire-impact/request.json @@ -0,0 +1,24 @@ +{ + "task": "assess wildfire impact around Lahaina after the August 2023 fire", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [-156.72, 20.84], + [-156.64, 20.84], + [-156.64, 20.93], + [-156.72, 20.93], + [-156.72, 20.84] + ] + ] + }, + "datetime": { + "start": "2023-08-09T00:00:00Z", + "end": "2023-08-20T23:59:59Z" + }, + "data_type": "optical", + "required_measurements": ["nir", "swir22"], + "max_source_resolution_m": 20, + "target_crs": "utm", + "target_resolution_m": 20 +}