xarray-spatial 0.9.6__tar.gz → 0.9.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/rockout.md +5 -13
- xarray_spatial-0.9.7/.claude/commands/sweep-accuracy.md +257 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/sweep-performance.md +25 -17
- xarray_spatial-0.9.7/.claude/commands/sweep-security.md +253 -0
- xarray_spatial-0.9.7/.claude/sweep-accuracy-state.json +22 -0
- xarray_spatial-0.9.7/.claude/sweep-performance-state.json +50 -0
- xarray_spatial-0.9.7/.claude/sweep-security-state.json +108 -0
- xarray_spatial-0.9.7/.efficiency-audit-baseline.json +171 -0
- xarray_spatial-0.9.7/.efficiency-audit-baseline.prev.json +138 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.github/ISSUE_TEMPLATE/feature-proposal.md +0 -1
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.gitignore +1 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/CHANGELOG.md +47 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/PKG-INFO +19 -3
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/README.md +18 -2
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xarray_spatial.egg-info/PKG-INFO +19 -3
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xarray_spatial.egg-info/SOURCES.txt +17 -3
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/__init__.py +5 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/_version.py +3 -3
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/accessor.py +9 -1
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/balanced_allocation.py +2 -2
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/bilateral.py +13 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/bump.py +141 -49
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/classify.py +127 -43
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/contour.py +31 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/convolution.py +47 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/cost_distance.py +1 -1
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/__init__.py +160 -42
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/_compression.py +0 -55
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/_gpu_decode.py +99 -8
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/_header.py +64 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/_reader.py +99 -13
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/_vrt.py +41 -3
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/_writer.py +60 -11
- xarray_spatial-0.9.7/xrspatial/geotiff/tests/test_cog.py +379 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_features.py +229 -2
- xarray_spatial-0.9.7/xrspatial/geotiff/tests/test_predictor_multisample.py +398 -0
- xarray_spatial-0.9.7/xrspatial/geotiff/tests/test_security.py +519 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hillshade.py +56 -25
- xarray_spatial-0.9.7/xrspatial/kde.py +747 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/pathfinding.py +6 -2
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/perlin.py +10 -0
- xarray_spatial-0.9.7/xrspatial/polygon_clip.py +272 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/polygonize.py +560 -5
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/rasterize.py +37 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/__init__.py +45 -22
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_grid.py +11 -0
- xarray_spatial-0.9.7/xrspatial/resample.py +791 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/sieve.py +126 -96
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_balanced_allocation.py +39 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_bilateral.py +40 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_bump.py +130 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_classify.py +44 -4
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_contour.py +38 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_cost_distance.py +41 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_focal.py +24 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_hillshade.py +73 -20
- xarray_spatial-0.9.7/xrspatial/tests/test_hypsometric_integral.py +256 -0
- xarray_spatial-0.9.7/xrspatial/tests/test_kde.py +461 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_perlin.py +27 -0
- xarray_spatial-0.9.7/xrspatial/tests/test_polygon_clip.py +360 -0
- xarray_spatial-0.9.7/xrspatial/tests/test_polygonize.py +1070 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_rasterize.py +49 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_reproject.py +59 -0
- xarray_spatial-0.9.7/xrspatial/tests/test_resample.py +380 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_sieve.py +18 -22
- xarray_spatial-0.9.7/xrspatial/tests/test_sieve_gdal_parity.py +467 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_viewshed.py +42 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_visibility.py +14 -11
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_zonal.py +39 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/viewshed.py +20 -2
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/visibility.py +114 -56
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/zonal.py +203 -1
- xarray_spatial-0.9.6/.claude/accuracy-sweep-state.json +0 -9
- xarray_spatial-0.9.6/.claude/commands/accuracy-sweep.md +0 -158
- xarray_spatial-0.9.6/.claude/performance-sweep-state.json +0 -47
- xarray_spatial-0.9.6/xrspatial/geotiff/tests/test_cog.py +0 -137
- xarray_spatial-0.9.6/xrspatial/tests/test_polygonize.py +0 -540
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/backend-parity.md +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/bench.md +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/dask-notebook.md +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/efficiency-audit.md +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/new-issues.md +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/release-major.md +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/release-minor.md +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/release-patch.md +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/review-pr.md +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/user-guide-notebook.md +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/validate.md +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.gitattributes +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.github/labeler.yml +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.github/pull_request_template.md +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.github/workflows/benchmarks.yml +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.github/workflows/labeler.yml +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.github/workflows/pypi-publish.yml +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.github/workflows/test.yml +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.readthedocs.yml +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/CODE_OF_CONDUCT.md +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/CONTRIBUTING.md +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/Citation-styles.md +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/LICENSE.txt +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/MANIFEST.in +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/RELEASE.md +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/codecov.yml +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/pyproject.toml +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/setup.cfg +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/setup.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xarray_spatial.egg-info/dependency_links.txt +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xarray_spatial.egg-info/entry_points.txt +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xarray_spatial.egg-info/not-zip-safe +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xarray_spatial.egg-info/requires.txt +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xarray_spatial.egg-info/top_level.txt +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/__main__.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/analytics.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/aspect.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/corridor.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/curvature.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/dasymetric.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/dataset_support.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/datasets/__init__.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/datasets/sentinel-2/blue_band.nc +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/datasets/sentinel-2/green_band.nc +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/datasets/sentinel-2/nir_band.nc +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/datasets/sentinel-2/red_band.nc +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/datasets/sentinel-2/swir1_band.nc +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/datasets/sentinel-2/swir2_band.nc +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/diagnostics.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/diffusion.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/edge_detection.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/emerging_hotspots.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/erosion.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/experimental/__init__.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/experimental/min_observable_height.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/fire.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/flood.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/focal.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geodesic.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/_dtypes.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/_geotags.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/__init__.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/bench_vs_rioxarray.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/conftest.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_accessor_io.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_accuracy_1081.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_compression.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_compression_level.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_dtype_read.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_edge_cases.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_geotags.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_header.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_jpeg.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_jpeg2000.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_lerc.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_lz4.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_reader.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_streaming_write.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_vrt_write.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_writer.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/glcm.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/gpu_rtx/__init__.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/gpu_rtx/cuda_utils.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/gpu_rtx/hillshade.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/gpu_rtx/mesh_utils.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/gpu_rtx/viewshed.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/__init__.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/_boundary_store.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/basin_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/fill_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_accumulation_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_accumulation_dinf.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_accumulation_mfd.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_direction_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_direction_dinf.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_direction_mfd.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_length_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_length_dinf.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_length_mfd.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_path_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_path_dinf.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_path_mfd.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/hand_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/hand_dinf.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/hand_mfd.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/sink_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/snap_pour_point_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/stream_link_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/stream_link_dinf.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/stream_link_mfd.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/stream_order_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/stream_order_dinf.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/stream_order_mfd.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/__init__.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/conftest.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_basin_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_fill_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_accumulation_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_accumulation_dinf.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_accumulation_mfd.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_direction_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_direction_dinf.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_direction_mfd.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_length_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_length_dinf.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_length_mfd.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_path_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_path_dinf.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_path_mfd.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_hand_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_hand_dinf.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_hand_mfd.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_sink_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_snap_pour_point_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_stream_link_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_stream_link_dinf.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_stream_link_mfd.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_stream_order_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_stream_order_dinf.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_stream_order_mfd.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_twi_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_watershed_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_watershed_dinf.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_watershed_mfd.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/twi_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/watershed_d8.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/watershed_dinf.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/watershed_mfd.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/interpolate/__init__.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/interpolate/_idw.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/interpolate/_kriging.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/interpolate/_spline.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/interpolate/_validation.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/mahalanobis.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/mcda/__init__.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/mcda/combine.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/mcda/constrain.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/mcda/sensitivity.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/mcda/standardize.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/mcda/weights.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/morphology.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/multispectral.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/normalize.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/preview.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/proximity.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_crs_utils.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_datum_grids.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_interpolate.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_itrf.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_lite_crs.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_merge.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_projections.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_projections_cuda.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_transform.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_vertical.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/at_bev_AT_GIS_GRID.tif +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/au_icsm_A66_National_13_09_01.tif +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/be_ign_bd72lb72_etrs89lb08.tif +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/ch_swisstopo_CHENyx06_ETRS.tif +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/de_adv_BETA2007.tif +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/es_ign_SPED2ETV2.tif +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/nl_nsgi_rdcorr2018.tif +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/pt_dgt_D73_ETRS89_geo.tif +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/uk_os_OSTN15_NTv2_OSGBtoETRS.tif +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/us_nga_egm96_15.tif +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/us_noaa_alaska.tif +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/us_noaa_conus.tif +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/us_noaa_hawaii.tif +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/us_noaa_nadcon5_nad27_nad83_1986_conus.tif +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/us_noaa_prvi.tif +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/sky_view_factor.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/slope.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/surface_distance.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/terrain.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/terrain_metrics.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/__init__.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/bench_reproject_vs_rioxarray.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/conftest.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/general_checks.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_accessor.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_analytics.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_aspect.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_corridor.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_curvature.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_dask_cupy_gaps.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_dask_laziness.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_dasymetric.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_dataset_support.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_datasets.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_diagnostics.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_diffusion.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_edge_detection.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_emerging_hotspots.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_erosion.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_fire.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_flood.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_fused_overlap.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_geodesic_aspect.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_geodesic_slope.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_glcm.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_glcm_metric_order.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_interpolation.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_lite_crs.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_mahalanobis.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_mcda.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_min_observable_height.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_morphology.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_morphology_derived.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_multi_overlap.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_multispectral.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_normalize.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_northness_eastness.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_pathfinding.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_preview.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_proximity.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_rasterize_accuracy.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_rechunk_no_shuffle.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_sky_view_factor.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_slope.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_surface_distance.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_terrain.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_terrain_metrics.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_utils.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_validation.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/utils.py +0 -0
- {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/worley.py +0 -0
|
@@ -14,7 +14,8 @@ through all seven steps below. The prompt is: $ARGUMENTS
|
|
|
14
14
|
2. Pick labels from the repo's existing set. Always include the type label
|
|
15
15
|
(`enhancement`, `bug`, or `proposal`). Add topical labels when they fit
|
|
16
16
|
(e.g. `gpu`, `performance`, `focal tools`, `hydrology`, etc.).
|
|
17
|
-
3. Draft the title and body. Use the repo's issue templates as structure guides
|
|
17
|
+
3. Draft the title and body. Use the repo's issue templates as structure guides
|
|
18
|
+
(skip the "Author of Proposal" field -- GitHub already shows the author):
|
|
18
19
|
- Enhancement/proposal: follow `.github/ISSUE_TEMPLATE/feature-proposal.md`
|
|
19
20
|
- Bug: follow `.github/ISSUE_TEMPLATE/bug_report.md`
|
|
20
21
|
4. **Run the body text through the `/humanizer` skill** before creating the issue
|
|
@@ -70,20 +71,11 @@ through all seven steps below. The prompt is: $ARGUMENTS
|
|
|
70
71
|
|
|
71
72
|
## Step 6 -- Create a User Guide Notebook
|
|
72
73
|
|
|
73
|
-
The project has an `examples/user_guide/` directory with numbered notebooks.
|
|
74
|
-
|
|
75
|
-
1. Determine the next available notebook number by listing the directory.
|
|
76
|
-
2. Create a new `.ipynb` notebook following the established pattern:
|
|
77
|
-
- Markdown cell with title and explanation of the feature
|
|
78
|
-
- Import cell
|
|
79
|
-
- Synthetic data generation with visualization
|
|
80
|
-
- Demonstrate each mode/option of the feature
|
|
81
|
-
- Show a practical use case or comparison
|
|
82
|
-
3. Use `matplotlib` for plots, consistent with existing notebooks.
|
|
83
|
-
4. Keep the notebook self-contained (no external data dependencies).
|
|
84
|
-
|
|
85
74
|
**Skip this step** if the change is a pure bug fix with no new user-facing API.
|
|
86
75
|
|
|
76
|
+
Run the `/user-guide-notebook` skill to create the notebook. It handles structure,
|
|
77
|
+
plotting conventions, GIS alert boxes, preview images, and humanizer passes.
|
|
78
|
+
|
|
87
79
|
## Step 7 -- Update the README Feature Matrix
|
|
88
80
|
|
|
89
81
|
1. Open `README.md` and find the appropriate category section in the feature matrix.
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# Accuracy Sweep: Dispatch subagents to audit modules for numerical accuracy issues
|
|
2
|
+
|
|
3
|
+
Audit xrspatial modules for numerical accuracy issues: floating point
|
|
4
|
+
precision loss, incorrect NaN propagation, off-by-one errors in neighborhood
|
|
5
|
+
operations, missing or wrong Earth curvature corrections, and backend
|
|
6
|
+
inconsistencies (numpy vs cupy vs dask results differ). Subagents fix
|
|
7
|
+
findings via /rockout.
|
|
8
|
+
|
|
9
|
+
Optional arguments: $ARGUMENTS
|
|
10
|
+
(e.g. `--top 3`, `--exclude slope,aspect`, `--only-terrain`, `--reset-state`)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Step 1 -- Gather module metadata via git
|
|
15
|
+
|
|
16
|
+
Enumerate candidate modules:
|
|
17
|
+
|
|
18
|
+
**Single-file modules:** Every `.py` file directly under `xrspatial/`, excluding
|
|
19
|
+
`__init__.py`, `_version.py`, `__main__.py`, `utils.py`, `accessor.py`,
|
|
20
|
+
`preview.py`, `dataset_support.py`, `diagnostics.py`, `analytics.py`.
|
|
21
|
+
|
|
22
|
+
**Subpackage modules:** `geotiff/`, `reproject/`, and `hydro/` directories under
|
|
23
|
+
`xrspatial/`. Treat each as a single audit unit. List all `.py` files within
|
|
24
|
+
each (excluding `__init__.py`).
|
|
25
|
+
|
|
26
|
+
For every module, collect:
|
|
27
|
+
|
|
28
|
+
| Field | How |
|
|
29
|
+
|-------|-----|
|
|
30
|
+
| **last_modified** | `git log -1 --format=%aI -- <path>` (for subpackages, most recent file) |
|
|
31
|
+
| **total_commits** | `git log --oneline -- <path> \| wc -l` |
|
|
32
|
+
| **loc** | `wc -l < <path>` (for subpackages, sum all files) |
|
|
33
|
+
| **recent_accuracy_commits** | `git log --oneline --grep='accuracy\|precision\|numerical\|geodesic' -- <path>` |
|
|
34
|
+
|
|
35
|
+
Store results in memory -- do NOT write intermediate files.
|
|
36
|
+
|
|
37
|
+
## Step 2 -- Load inspection state
|
|
38
|
+
|
|
39
|
+
Read `.claude/sweep-accuracy-state.json`.
|
|
40
|
+
|
|
41
|
+
If it does not exist, treat every module as never-inspected.
|
|
42
|
+
|
|
43
|
+
If `$ARGUMENTS` contains `--reset-state`, delete the file and treat
|
|
44
|
+
everything as never-inspected.
|
|
45
|
+
|
|
46
|
+
State file schema:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"inspections": {
|
|
51
|
+
"slope": {
|
|
52
|
+
"last_inspected": "2026-03-28T14:00:00Z",
|
|
53
|
+
"issue": 1042,
|
|
54
|
+
"severity_max": "HIGH",
|
|
55
|
+
"categories_found": [1, 3]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Step 3 -- Score each module
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
days_since_inspected = (today - last_inspected).days # 9999 if never
|
|
65
|
+
days_since_modified = (today - last_modified).days
|
|
66
|
+
has_recent_accuracy_work = 1 if recent_accuracy_commits is non-empty, else 0
|
|
67
|
+
|
|
68
|
+
score = (days_since_inspected * 3)
|
|
69
|
+
+ (total_commits * 0.5)
|
|
70
|
+
- (days_since_modified * 0.2)
|
|
71
|
+
- (has_recent_accuracy_work * 500)
|
|
72
|
+
+ (loc * 0.05)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Rationale:
|
|
76
|
+
- Modules never inspected dominate (9999 * 3)
|
|
77
|
+
- More commits = more complex = more likely to have accuracy bugs
|
|
78
|
+
- Recently modified modules slightly deprioritized (someone just touched them)
|
|
79
|
+
- Modules with existing accuracy work heavily deprioritized
|
|
80
|
+
- Larger files have more surface area (0.05 per line)
|
|
81
|
+
|
|
82
|
+
## Step 4 -- Apply filters from $ARGUMENTS
|
|
83
|
+
|
|
84
|
+
- `--top N` -- only audit the top N modules (default: 3)
|
|
85
|
+
- `--exclude mod1,mod2` -- remove named modules from the list
|
|
86
|
+
- `--only-terrain` -- restrict to: slope, aspect, curvature, terrain,
|
|
87
|
+
terrain_metrics, hillshade, sky_view_factor
|
|
88
|
+
- `--only-focal` -- restrict to: focal, convolution, morphology, bilateral,
|
|
89
|
+
edge_detection, glcm
|
|
90
|
+
- `--only-hydro` -- restrict to: flood, cost_distance, geodesic,
|
|
91
|
+
surface_distance, viewshed, erosion, diffusion, hydro (subpackage)
|
|
92
|
+
- `--only-io` -- restrict to: geotiff, reproject, rasterize, polygonize
|
|
93
|
+
|
|
94
|
+
## Step 5 -- Print the ranked table and launch subagents
|
|
95
|
+
|
|
96
|
+
### 5a. Print the ranked table
|
|
97
|
+
|
|
98
|
+
Print a markdown table showing ALL scored modules (not just selected ones),
|
|
99
|
+
sorted by score descending:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
| Rank | Module | Score | Last Inspected | Last Modified | Commits | LOC |
|
|
103
|
+
|------|-----------------|--------|----------------|---------------|---------|------|
|
|
104
|
+
| 1 | viewshed | 30012 | never | 45 days ago | 23 | 800 |
|
|
105
|
+
| 2 | flood | 29998 | never | 120 days ago | 18 | 600 |
|
|
106
|
+
| ... | ... | ... | ... | ... | ... | ... |
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 5b. Launch subagents for the top N modules
|
|
110
|
+
|
|
111
|
+
For each of the top N modules (default 3), launch an Agent in parallel using
|
|
112
|
+
`isolation: "worktree"` and `mode: "auto"`. All N agents must be dispatched
|
|
113
|
+
in a single message so they run concurrently.
|
|
114
|
+
|
|
115
|
+
Each agent's prompt must be self-contained and follow this template (adapt
|
|
116
|
+
the module name, paths, and metadata):
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
You are auditing the xrspatial module "{module}" for numerical accuracy issues.
|
|
120
|
+
|
|
121
|
+
This module has {commits} commits and {loc} lines of code.
|
|
122
|
+
|
|
123
|
+
Read these files: {module_files}
|
|
124
|
+
|
|
125
|
+
Also read xrspatial/utils.py to understand _validate_raster() behavior and
|
|
126
|
+
xrspatial/tests/general_checks.py for the cross-backend comparison helpers.
|
|
127
|
+
|
|
128
|
+
**Your task:**
|
|
129
|
+
|
|
130
|
+
1. Read all listed files thoroughly, including the matching test file(s)
|
|
131
|
+
under xrspatial/tests/ so you understand expected behavior.
|
|
132
|
+
|
|
133
|
+
2. Audit for these 5 accuracy categories. For each, look for the specific
|
|
134
|
+
patterns described. Only flag issues ACTUALLY present in the code.
|
|
135
|
+
|
|
136
|
+
**Cat 1 — Floating Point Precision Loss**
|
|
137
|
+
- Accumulation loops that sum many small values into a large running
|
|
138
|
+
total without Kahan summation or compensated accumulation
|
|
139
|
+
- float32 used where float64 is required for stable intermediate results
|
|
140
|
+
(e.g. large grids, long gradients, iterative solvers)
|
|
141
|
+
- Subtraction of nearly-equal large quantities (catastrophic cancellation)
|
|
142
|
+
- Division by small numbers without a stability floor
|
|
143
|
+
Severity: HIGH if the result is visibly wrong on realistic inputs;
|
|
144
|
+
MEDIUM if only observable on adversarial inputs
|
|
145
|
+
|
|
146
|
+
**Cat 2 — NaN / Inf Propagation Errors**
|
|
147
|
+
- NaN input silently produces a finite output (masked, skipped, or
|
|
148
|
+
treated as zero without being documented)
|
|
149
|
+
- NaN check using `==` instead of `!= x` for NaN detection in numba
|
|
150
|
+
- Neighborhood operations that ignore NaN pixels but do not update the
|
|
151
|
+
normalization denominator, biasing the result
|
|
152
|
+
- Inf / -Inf inputs treated as numbers in comparisons without guards
|
|
153
|
+
- Divide-by-zero producing Inf that then corrupts downstream accumulation
|
|
154
|
+
Severity: HIGH if NaN input yields a wrong but finite output;
|
|
155
|
+
MEDIUM if the behavior is documented but still surprising
|
|
156
|
+
|
|
157
|
+
**Cat 3 — Off-by-One Errors in Neighborhood Operations**
|
|
158
|
+
- Loop bounds that exclude the last row/column (e.g. `range(H-1)` where
|
|
159
|
+
`range(H)` is intended)
|
|
160
|
+
- `map_overlap` depth that is smaller than the actual stencil radius
|
|
161
|
+
- Boundary handling that duplicates or skips edge pixels
|
|
162
|
+
- Asymmetric kernel indexing (one-sided rather than centered)
|
|
163
|
+
- CUDA kernel bounds guard that is `i > H` instead of `i >= H`
|
|
164
|
+
Severity: HIGH if it causes a silent wrong result at all chunk boundaries;
|
|
165
|
+
MEDIUM if it only affects a single-pixel edge
|
|
166
|
+
|
|
167
|
+
**Cat 4 — Missing or Wrong Earth Curvature / Projection Corrections**
|
|
168
|
+
- Geodesic calculations that assume a flat projection without curvature
|
|
169
|
+
correction (see slope.py, aspect.py, geodesic.py for the reference
|
|
170
|
+
pattern: `u += (e² + n²) / (2R)`)
|
|
171
|
+
- Haversine / great-circle distance using the wrong Earth radius
|
|
172
|
+
constant, or using a spherical approximation where WGS84 is needed
|
|
173
|
+
- Mixing projected and geographic coordinates in the same calculation
|
|
174
|
+
without a transform
|
|
175
|
+
- Using cell size in degrees as if it were meters
|
|
176
|
+
Severity: HIGH if the correction is missing entirely on a public API;
|
|
177
|
+
MEDIUM if the correction is present but uses a questionable constant
|
|
178
|
+
|
|
179
|
+
**Cat 5 — Backend Inconsistency (numpy vs cupy vs dask)**
|
|
180
|
+
- numpy and cupy paths use different algorithms that can diverge on
|
|
181
|
+
identical inputs (e.g. different boundary handling, different NaN
|
|
182
|
+
semantics, different numerical precision)
|
|
183
|
+
- dask path silently falls back to materializing the full array
|
|
184
|
+
- dask `map_overlap` chunk function returns a different shape than the
|
|
185
|
+
input, corrupting the reassembled array
|
|
186
|
+
- A backend raises on valid input that another backend accepts
|
|
187
|
+
- Result dtype differs across backends without documentation
|
|
188
|
+
Severity: HIGH if numerically different results on the same input;
|
|
189
|
+
MEDIUM if only metadata (dtype, coords) differs
|
|
190
|
+
|
|
191
|
+
3. For each real issue found, assign a severity (CRITICAL/HIGH/MEDIUM/LOW)
|
|
192
|
+
and note the exact file and line number.
|
|
193
|
+
|
|
194
|
+
4. If any CRITICAL or HIGH issue is found, run /rockout to fix it end-to-end
|
|
195
|
+
(GitHub issue, worktree branch, fix, tests, and PR).
|
|
196
|
+
For MEDIUM/LOW issues, document them but do not fix.
|
|
197
|
+
|
|
198
|
+
5. After finishing (whether you found issues or not), update the inspection
|
|
199
|
+
state file .claude/sweep-accuracy-state.json by reading its current
|
|
200
|
+
contents and adding/updating the entry for "{module}" with:
|
|
201
|
+
- "last_inspected": today's ISO date
|
|
202
|
+
- "issue": the issue number from rockout (or null if clean / MEDIUM-only)
|
|
203
|
+
- "severity_max": highest severity found (or null if clean)
|
|
204
|
+
- "categories_found": list of category numbers that had findings (e.g. [1, 3])
|
|
205
|
+
|
|
206
|
+
Then `git add .claude/sweep-accuracy-state.json` and commit it to the
|
|
207
|
+
worktree branch so the state update is included in the PR.
|
|
208
|
+
|
|
209
|
+
Important:
|
|
210
|
+
- Only flag real accuracy issues. False positives waste time.
|
|
211
|
+
- Read the tests for this module to understand expected behavior before
|
|
212
|
+
flagging a result as wrong -- the test may codify the current behavior.
|
|
213
|
+
- For backend comparisons, check that the cross-backend tests in
|
|
214
|
+
xrspatial/tests/general_checks.py actually exercise the code path you
|
|
215
|
+
are suspicious of; missing test coverage is itself a finding.
|
|
216
|
+
- Do NOT flag the use of numba @jit itself as an accuracy issue. Focus on
|
|
217
|
+
what the JIT code does, not that it uses JIT.
|
|
218
|
+
- For the hydro subpackage: focus on one representative variant (d8) in
|
|
219
|
+
detail, then note which dinf/mfd files share the same pattern. Do not
|
|
220
|
+
read all 29 files line by line.
|
|
221
|
+
- This repo uses ArrayTypeFunctionMapping to dispatch across numpy/cupy/dask
|
|
222
|
+
backends. Check all backend paths, not just numpy.
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### 5c. Print a status line
|
|
226
|
+
|
|
227
|
+
After dispatching, print:
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
Launched {N} accuracy audit agents: {module1}, {module2}, {module3}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Step 6 -- State updates
|
|
234
|
+
|
|
235
|
+
State is updated by the subagents themselves (see agent prompt step 5).
|
|
236
|
+
After completion, verify state with:
|
|
237
|
+
|
|
238
|
+
```
|
|
239
|
+
cat .claude/sweep-accuracy-state.json
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
To reset all tracking: `/sweep-accuracy --reset-state`
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## General Rules
|
|
247
|
+
|
|
248
|
+
- Do NOT modify any source files directly. Subagents handle fixes via /rockout.
|
|
249
|
+
- Keep the output concise -- the table and agent dispatch are the deliverables.
|
|
250
|
+
- If $ARGUMENTS is empty, use defaults: top 3, no category filter, no exclusions.
|
|
251
|
+
- State file (`.claude/sweep-accuracy-state.json`) is tracked in git.
|
|
252
|
+
Subagents must `git add` and commit it so the state update lands in the PR.
|
|
253
|
+
- For subpackage modules (geotiff, reproject, hydro), the subagent should read
|
|
254
|
+
ALL `.py` files in the subpackage directory, not just `__init__.py`.
|
|
255
|
+
- Only flag patterns that are ACTUALLY present in the code. Do not report
|
|
256
|
+
hypothetical issues or patterns that "could" occur with imaginary inputs.
|
|
257
|
+
- False positives are worse than missed issues. When in doubt, skip.
|
|
@@ -22,7 +22,7 @@ Parse $ARGUMENTS for these flags (multiple may combine):
|
|
|
22
22
|
| `--only-focal` | Restrict to: focal, convolution, morphology, bilateral, edge_detection, glcm |
|
|
23
23
|
| `--only-hydro` | Restrict to: flood, cost_distance, geodesic, surface_distance, viewshed, erosion, diffusion |
|
|
24
24
|
| `--only-io` | Restrict to: geotiff, reproject, rasterize, polygonize |
|
|
25
|
-
| `--reset-state` | Delete `.claude/performance-
|
|
25
|
+
| `--reset-state` | Delete `.claude/sweep-performance-state.json` and treat all modules as never-inspected |
|
|
26
26
|
| `--skip-phase1` | Skip triage; reuse last state file; go straight to ralph-loop generation for unresolved HIGH items |
|
|
27
27
|
| `--report-only` | Run Phase 1 triage but do not generate a ralph-loop command |
|
|
28
28
|
| `--size small` | Phase 2 benchmarks use 128x128 arrays |
|
|
@@ -64,7 +64,7 @@ For every module in scope, collect:
|
|
|
64
64
|
|
|
65
65
|
### Load inspection state
|
|
66
66
|
|
|
67
|
-
Read `.claude/performance-
|
|
67
|
+
Read `.claude/sweep-performance-state.json`. If it does not exist, treat every
|
|
68
68
|
module as never-inspected. If `--reset-state` was set, delete the file first.
|
|
69
69
|
|
|
70
70
|
State file schema:
|
|
@@ -344,7 +344,7 @@ rockout has full context.
|
|
|
344
344
|
|
|
345
345
|
## Step 5 -- Update state file
|
|
346
346
|
|
|
347
|
-
Write `.claude/performance-
|
|
347
|
+
Write `.claude/sweep-performance-state.json` with the triage results:
|
|
348
348
|
|
|
349
349
|
```json
|
|
350
350
|
{
|
|
@@ -395,12 +395,20 @@ If no modules qualify, print:
|
|
|
395
395
|
`--skip-phase1` to refresh the triage."
|
|
396
396
|
Then stop.
|
|
397
397
|
|
|
398
|
-
### 6b.
|
|
398
|
+
### 6b. Invoke the ralph-loop automatically
|
|
399
399
|
|
|
400
|
-
Using the target list,
|
|
400
|
+
Using the target list, invoke the ralph-loop skill directly via the Skill tool.
|
|
401
|
+
Do NOT print the command for manual copy-paste — execute it immediately.
|
|
401
402
|
|
|
402
|
-
|
|
403
|
-
|
|
403
|
+
Set `--max-iterations` to the number of target modules plus 2 (buffer for
|
|
404
|
+
retries).
|
|
405
|
+
|
|
406
|
+
Call the Skill tool with `skill: "ralph-loop"` and pass the following as `args`
|
|
407
|
+
(with `<module>`, `<N>`, `<OOM verdict>`, `<SIZE>`, and `<N+2>` substituted
|
|
408
|
+
from the actual triage results):
|
|
409
|
+
|
|
410
|
+
```
|
|
411
|
+
"Performance sweep Phase 2: benchmark and fix HIGH-severity findings.
|
|
404
412
|
|
|
405
413
|
**Target modules in priority order:**
|
|
406
414
|
1. <module> (<N> HIGH findings, <OOM verdict>) -- <one-line summary of worst finding>
|
|
@@ -440,25 +448,25 @@ Using the target list, generate and print:
|
|
|
440
448
|
| dask+numpy | peak_rss_mb | 892 | 34 | 0.04x | IMPROVED |
|
|
441
449
|
Thresholds: IMPROVED < 0.8x, REGRESSION > 1.2x, else UNCHANGED.
|
|
442
450
|
|
|
443
|
-
6. Update .claude/performance-
|
|
451
|
+
6. Update .claude/sweep-performance-state.json with the issue number, then
|
|
452
|
+
`git add` and commit it to the worktree branch so the state update is
|
|
453
|
+
included in the PR.
|
|
444
454
|
|
|
445
455
|
7. Output <promise>ITERATION DONE</promise>
|
|
446
456
|
|
|
447
457
|
If all targets have been addressed or confirmed as false positives:
|
|
448
458
|
<promise>ALL PERFORMANCE ISSUES FIXED</promise>." --max-iterations <N+2> --completion-promise "ALL PERFORMANCE ISSUES FIXED"
|
|
449
|
-
|
|
459
|
+
```
|
|
450
460
|
|
|
451
|
-
|
|
452
|
-
retries).
|
|
461
|
+
### 6c. Print status
|
|
453
462
|
|
|
454
|
-
|
|
463
|
+
After invoking the ralph-loop, print:
|
|
455
464
|
|
|
456
465
|
```
|
|
457
|
-
Phase
|
|
458
|
-
Copy the ralph-loop command above and paste it.
|
|
466
|
+
Phase 2 ralph-loop launched with <N> target modules.
|
|
459
467
|
|
|
460
468
|
Other options:
|
|
461
|
-
Fix one manually: copy any /rockout command from the report above
|
|
469
|
+
Fix one manually: copy any /rockout command from the triage report above
|
|
462
470
|
Rerun triage only: /sweep-performance --report-only
|
|
463
471
|
Skip Phase 1: /sweep-performance --skip-phase1 (reuses last triage)
|
|
464
472
|
Reset all tracking: /sweep-performance --reset-state
|
|
@@ -483,8 +491,8 @@ Other options:
|
|
|
483
491
|
on a known-numpy array), do not flag it.
|
|
484
492
|
- The 30TB simulation constructs the dask task graph only; it NEVER calls
|
|
485
493
|
`.compute()`.
|
|
486
|
-
- State file (`.claude/performance-
|
|
487
|
-
|
|
494
|
+
- State file (`.claude/sweep-performance-state.json`) is tracked in git.
|
|
495
|
+
Subagents must `git add` and commit it so the state update lands in the PR.
|
|
488
496
|
- If $ARGUMENTS is empty, use defaults: audit all modules, benchmark at
|
|
489
497
|
512x512, generate ralph-loop for HIGH items.
|
|
490
498
|
- For subpackage modules (geotiff, reproject), the subagent should read ALL
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# Security Sweep: Dispatch subagents to audit modules for security vulnerabilities
|
|
2
|
+
|
|
3
|
+
Audit xrspatial modules for security issues specific to numeric/GPU raster
|
|
4
|
+
libraries: unbounded allocations, integer overflow, NaN logic bombs, GPU
|
|
5
|
+
kernel bounds, file path injection, and dtype confusion. Subagents fix
|
|
6
|
+
CRITICAL/HIGH issues via /rockout.
|
|
7
|
+
|
|
8
|
+
Optional arguments: $ARGUMENTS
|
|
9
|
+
(e.g. `--top 3`, `--exclude slope,aspect`, `--only-io`, `--reset-state`)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Step 1 -- Gather module metadata via git and grep
|
|
14
|
+
|
|
15
|
+
Enumerate candidate modules:
|
|
16
|
+
|
|
17
|
+
**Single-file modules:** Every `.py` file directly under `xrspatial/`, excluding
|
|
18
|
+
`__init__.py`, `_version.py`, `__main__.py`, `utils.py`, `accessor.py`,
|
|
19
|
+
`preview.py`, `dataset_support.py`, `diagnostics.py`, `analytics.py`.
|
|
20
|
+
|
|
21
|
+
**Subpackage modules:** `geotiff/`, `reproject/`, and `hydro/` directories under
|
|
22
|
+
`xrspatial/`. Treat each as a single audit unit. List all `.py` files within
|
|
23
|
+
each (excluding `__init__.py`).
|
|
24
|
+
|
|
25
|
+
For every module, collect:
|
|
26
|
+
|
|
27
|
+
| Field | How |
|
|
28
|
+
|-------|-----|
|
|
29
|
+
| **last_modified** | `git log -1 --format=%aI -- <path>` (for subpackages, most recent file) |
|
|
30
|
+
| **total_commits** | `git log --oneline -- <path> \| wc -l` |
|
|
31
|
+
| **loc** | `wc -l < <path>` (for subpackages, sum all files) |
|
|
32
|
+
| **has_cuda_kernels** | grep file(s) for `@cuda.jit` |
|
|
33
|
+
| **has_file_io** | grep file(s) for `open(`, `mkstemp`, `os.path`, `pathlib` |
|
|
34
|
+
| **has_numba_jit** | grep file(s) for `@ngjit`, `@njit`, `@jit`, `numba.jit` |
|
|
35
|
+
| **allocates_from_dims** | grep file(s) for `np.empty(height`, `np.zeros(height`, `np.empty(H`, `np.empty(h `, `cp.empty(`, and width variants |
|
|
36
|
+
| **has_shared_memory** | grep file(s) for `cuda.shared.array` |
|
|
37
|
+
|
|
38
|
+
Store results in memory -- do NOT write intermediate files.
|
|
39
|
+
|
|
40
|
+
## Step 2 -- Load inspection state
|
|
41
|
+
|
|
42
|
+
Read `.claude/sweep-security-state.json`.
|
|
43
|
+
|
|
44
|
+
If it does not exist, treat every module as never-inspected.
|
|
45
|
+
|
|
46
|
+
If `$ARGUMENTS` contains `--reset-state`, delete the file and treat
|
|
47
|
+
everything as never-inspected.
|
|
48
|
+
|
|
49
|
+
State file schema:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"inspections": {
|
|
54
|
+
"cost_distance": {
|
|
55
|
+
"last_inspected": "2026-04-10T14:00:00Z",
|
|
56
|
+
"issue": 1150,
|
|
57
|
+
"severity_max": "HIGH",
|
|
58
|
+
"categories_found": [1, 2]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Step 3 -- Score each module
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
days_since_inspected = (today - last_inspected).days # 9999 if never
|
|
68
|
+
days_since_modified = (today - last_modified).days
|
|
69
|
+
|
|
70
|
+
score = (days_since_inspected * 3)
|
|
71
|
+
+ (has_file_io * 400)
|
|
72
|
+
+ (allocates_from_dims * 300)
|
|
73
|
+
+ (has_cuda_kernels * 250)
|
|
74
|
+
+ (has_shared_memory * 200)
|
|
75
|
+
+ (has_numba_jit * 100)
|
|
76
|
+
+ (loc * 0.05)
|
|
77
|
+
- (days_since_modified * 0.2)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Rationale:
|
|
81
|
+
- File I/O is the only external-escape vector (400)
|
|
82
|
+
- Unbounded allocation is a DoS vector across all backends (300)
|
|
83
|
+
- CUDA bugs cause silent memory corruption (250)
|
|
84
|
+
- Shared memory overflow is a CUDA sub-risk (200)
|
|
85
|
+
- Numba JIT is ubiquitous -- lower weight avoids noise (100)
|
|
86
|
+
- Larger files have more surface area (0.05 per line)
|
|
87
|
+
- Recently modified code slightly deprioritized
|
|
88
|
+
|
|
89
|
+
## Step 4 -- Apply filters from $ARGUMENTS
|
|
90
|
+
|
|
91
|
+
- `--top N` -- only audit the top N modules (default: 3)
|
|
92
|
+
- `--exclude mod1,mod2` -- remove named modules from the list
|
|
93
|
+
- `--only-terrain` -- restrict to: slope, aspect, curvature, terrain,
|
|
94
|
+
terrain_metrics, hillshade, sky_view_factor
|
|
95
|
+
- `--only-focal` -- restrict to: focal, convolution, morphology, bilateral,
|
|
96
|
+
edge_detection, glcm
|
|
97
|
+
- `--only-hydro` -- restrict to: flood, cost_distance, geodesic,
|
|
98
|
+
surface_distance, viewshed, erosion, diffusion, hydro (subpackage)
|
|
99
|
+
- `--only-io` -- restrict to: geotiff, reproject, rasterize, polygonize
|
|
100
|
+
|
|
101
|
+
## Step 5 -- Print the ranked table and launch subagents
|
|
102
|
+
|
|
103
|
+
### 5a. Print the ranked table
|
|
104
|
+
|
|
105
|
+
Print a markdown table showing ALL scored modules (not just selected ones),
|
|
106
|
+
sorted by score descending:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
| Rank | Module | Score | Last Inspected | CUDA | FileIO | Alloc | Numba | LOC |
|
|
110
|
+
|------|-----------------|--------|----------------|------|--------|-------|-------|------|
|
|
111
|
+
| 1 | geotiff | 30600 | never | yes | yes | no | yes | 1400 |
|
|
112
|
+
| 2 | hydro | 30300 | never | yes | no | yes | yes | 8200 |
|
|
113
|
+
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 5b. Launch subagents for the top N modules
|
|
117
|
+
|
|
118
|
+
For each of the top N modules (default 3), launch an Agent in parallel using
|
|
119
|
+
`isolation: "worktree"` and `mode: "auto"`. All N agents must be dispatched
|
|
120
|
+
in a single message so they run concurrently.
|
|
121
|
+
|
|
122
|
+
Each agent's prompt must be self-contained and follow this template (adapt
|
|
123
|
+
the module name, paths, and metadata):
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
You are auditing the xrspatial module "{module}" for security vulnerabilities.
|
|
127
|
+
|
|
128
|
+
This module has {commits} commits and {loc} lines of code.
|
|
129
|
+
|
|
130
|
+
Read these files: {module_files}
|
|
131
|
+
|
|
132
|
+
Also read xrspatial/utils.py to understand _validate_raster() behavior.
|
|
133
|
+
|
|
134
|
+
**Your task:**
|
|
135
|
+
|
|
136
|
+
1. Read all listed files thoroughly.
|
|
137
|
+
|
|
138
|
+
2. Audit for these 6 security categories. For each, look for the specific
|
|
139
|
+
patterns described. Only flag issues ACTUALLY present in the code.
|
|
140
|
+
|
|
141
|
+
**Cat 1 — Unbounded Allocation / Denial of Service**
|
|
142
|
+
- np.empty(), np.zeros(), np.full() where size comes from array dimensions
|
|
143
|
+
(height*width, H*W, nrows*ncols) without a configurable max or memory check
|
|
144
|
+
- CuPy equivalents (cp.empty, cp.zeros)
|
|
145
|
+
- Queue/heap arrays sized at height*width without bounds validation
|
|
146
|
+
Severity: HIGH if no memory guard exists; MEDIUM if a partial guard exists
|
|
147
|
+
|
|
148
|
+
**Cat 2 — Integer Overflow in Index Math**
|
|
149
|
+
- height*width multiplication in int32 (overflows silently at ~46340x46340)
|
|
150
|
+
- Flat index calculations (r*width + c) in numba JIT without overflow check
|
|
151
|
+
- Queue index variables in int32 that could overflow for large arrays
|
|
152
|
+
Severity: HIGH for int32 overflow in production paths; MEDIUM for int64
|
|
153
|
+
overflow only possible with unrealistic dimensions (>3 billion pixels)
|
|
154
|
+
|
|
155
|
+
**Cat 3 — NaN/Inf as Logic Errors**
|
|
156
|
+
- Division without zero-check in numba kernels
|
|
157
|
+
- log/sqrt of potentially negative values without guard
|
|
158
|
+
- Accumulation loops that could hit Inf (summing many large values)
|
|
159
|
+
- Missing NaN propagation: NaN input silently produces finite output
|
|
160
|
+
- Incorrect NaN check: using == instead of != for NaN detection in numba
|
|
161
|
+
Severity: HIGH if in flood routing, erosion, viewshed, or cost_distance
|
|
162
|
+
(safety-critical modules); MEDIUM otherwise
|
|
163
|
+
|
|
164
|
+
**Cat 4 — GPU Kernel Bounds Safety**
|
|
165
|
+
- CUDA kernels missing `if i >= H or j >= W: return` bounds guard
|
|
166
|
+
- cuda.shared.array with fixed size that could overflow with adversarial
|
|
167
|
+
input parameters
|
|
168
|
+
- Missing cuda.syncthreads() after shared memory writes before reads
|
|
169
|
+
- Thread block dimensions that could cause register spill or launch failure
|
|
170
|
+
Severity: CRITICAL if bounds guard is missing (out-of-bounds GPU write);
|
|
171
|
+
HIGH for shared memory overflow or missing syncthreads
|
|
172
|
+
|
|
173
|
+
**Cat 5 — File Path Injection**
|
|
174
|
+
- File paths constructed from user strings without os.path.realpath() or
|
|
175
|
+
os.path.abspath() canonicalization
|
|
176
|
+
- Path traversal via ../ not prevented
|
|
177
|
+
- Temporary file creation in user-controlled directories
|
|
178
|
+
Severity: CRITICAL if user-provided path is used without any
|
|
179
|
+
canonicalization; HIGH if partial canonicalization is bypassable
|
|
180
|
+
|
|
181
|
+
**Cat 6 — Dtype Confusion**
|
|
182
|
+
- Public API functions that do NOT call _validate_raster() on their inputs
|
|
183
|
+
- Numba kernels that assume float64 but could receive float32 or int arrays
|
|
184
|
+
- Operations where dtype mismatch causes silent wrong results (not an error)
|
|
185
|
+
- CuPy/NumPy backend inconsistency in dtype handling
|
|
186
|
+
Severity: HIGH if wrong results are silent; MEDIUM if an error occurs but
|
|
187
|
+
the error message is misleading
|
|
188
|
+
|
|
189
|
+
3. For each real issue found, assign a severity (CRITICAL/HIGH/MEDIUM/LOW)
|
|
190
|
+
and note the exact file and line number.
|
|
191
|
+
|
|
192
|
+
4. If any CRITICAL or HIGH issue is found, run /rockout to fix it end-to-end
|
|
193
|
+
(GitHub issue, worktree branch, fix, tests, and PR).
|
|
194
|
+
For MEDIUM/LOW issues, document them but do not fix.
|
|
195
|
+
|
|
196
|
+
5. After finishing (whether you found issues or not), update the inspection
|
|
197
|
+
state file .claude/sweep-security-state.json by reading its current
|
|
198
|
+
contents and adding/updating the entry for "{module}" with:
|
|
199
|
+
- "last_inspected": today's ISO date
|
|
200
|
+
- "issue": the issue number from rockout (or null if clean / MEDIUM-only)
|
|
201
|
+
- "severity_max": highest severity found (or null if clean)
|
|
202
|
+
- "categories_found": list of category numbers that had findings (e.g. [1, 2])
|
|
203
|
+
|
|
204
|
+
Then `git add .claude/sweep-security-state.json` and commit it to the
|
|
205
|
+
worktree branch so the state update is included in the PR.
|
|
206
|
+
|
|
207
|
+
Important:
|
|
208
|
+
- Only flag real, exploitable issues. False positives waste time.
|
|
209
|
+
- Read the tests for this module to understand expected behavior.
|
|
210
|
+
- For CUDA code, verify bounds guards are truly missing -- many kernels already
|
|
211
|
+
have `if i >= H or j >= W: return`.
|
|
212
|
+
- Do NOT flag the use of numba @jit itself as a security issue. Focus on what
|
|
213
|
+
the JIT code does, not that it uses JIT.
|
|
214
|
+
- For the hydro subpackage: focus on one representative variant (d8) in detail,
|
|
215
|
+
then note which dinf/mfd files share the same pattern. Do not read all 29
|
|
216
|
+
files line by line.
|
|
217
|
+
- This repo uses ArrayTypeFunctionMapping to dispatch across numpy/cupy/dask
|
|
218
|
+
backends. Check all backend paths, not just numpy.
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### 5c. Print a status line
|
|
222
|
+
|
|
223
|
+
After dispatching, print:
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
Launched {N} security audit agents: {module1}, {module2}, {module3}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Step 6 -- State updates
|
|
230
|
+
|
|
231
|
+
State is updated by the subagents themselves (see agent prompt step 5).
|
|
232
|
+
After completion, verify state with:
|
|
233
|
+
|
|
234
|
+
```
|
|
235
|
+
cat .claude/sweep-security-state.json
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
To reset all tracking: `/sweep-security --reset-state`
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## General Rules
|
|
243
|
+
|
|
244
|
+
- Do NOT modify any source files directly. Subagents handle fixes via /rockout.
|
|
245
|
+
- Keep the output concise -- the table and agent dispatch are the deliverables.
|
|
246
|
+
- If $ARGUMENTS is empty, use defaults: top 3, no category filter, no exclusions.
|
|
247
|
+
- State file (`.claude/sweep-security-state.json`) is tracked in git.
|
|
248
|
+
Subagents must `git add` and commit it so the state update lands in the PR.
|
|
249
|
+
- For subpackage modules (geotiff, reproject, hydro), the subagent should read
|
|
250
|
+
ALL `.py` files in the subpackage directory, not just `__init__.py`.
|
|
251
|
+
- Only flag patterns that are ACTUALLY present in the code. Do not report
|
|
252
|
+
hypothetical issues or patterns that "could" occur with imaginary inputs.
|
|
253
|
+
- False positives are worse than missed issues. When in doubt, skip.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"inspections": {
|
|
3
|
+
"zonal": { "last_inspected": "2026-03-30T12:00:00Z", "issue": 1090 },
|
|
4
|
+
"focal": { "last_inspected": "2026-03-30T13:00:00Z", "issue": 1092 },
|
|
5
|
+
"multispectral": { "last_inspected": "2026-03-30T14:00:00Z", "issue": 1094 },
|
|
6
|
+
"proximity": { "last_inspected": "2026-03-30T15:00:00Z", "issue": null, "notes": "Direction >= boundary fragile but works due to truncated constant. Float32 truncation is design choice. No wrong-results bugs found." },
|
|
7
|
+
"curvature": { "last_inspected": "2026-03-30T15:00:00Z", "issue": null, "notes": "Formula matches ArcGIS reference. Backends consistent. No issues found." },
|
|
8
|
+
"hillshade": { "last_inspected": "2026-04-10T12:00:00Z", "issue": null, "notes": "Horn's method correct. All backends consistent. NaN propagation correct. float32 adequate for [0,1] output." },
|
|
9
|
+
"terrain": { "last_inspected": "2026-04-10T12:00:00Z", "issue": null, "notes": "Perlin/Worley/ridged noise correct. Dask chunk boundaries produce bit-identical results. No precision issues." },
|
|
10
|
+
"perlin": { "last_inspected": "2026-04-10T12:00:00Z", "issue": null, "notes": "Improved Perlin noise implementation correct. Fade/gradient functions verified. Backend-consistent. Continuous at cell boundaries." },
|
|
11
|
+
"sieve": { "last_inspected": "2026-04-13T12:00:00Z", "issue": null, "notes": "Union-find CCL correct. NaN excluded from labeling. All backends funnel through _sieve_numpy." },
|
|
12
|
+
"polygonize": { "last_inspected": "2026-04-13T12:00:00Z", "issue": 1190, "notes": "NaN pixels not masked in numpy/dask backends. Fix in PR #1194." },
|
|
13
|
+
"cost_distance": { "last_inspected": "2026-04-13T12:00:00Z", "issue": 1191, "notes": "CuPy Bellman-Ford max_iterations = h+w instead of h*w. Fix in PR #1192." },
|
|
14
|
+
"visibility": { "last_inspected": "2026-04-13T12:00:00Z", "issue": null, "notes": "Bresenham line, LOS kernel, Fresnel zone all correct. All backends converge to numpy." },
|
|
15
|
+
"kde": { "last_inspected": "2026-04-13T12:00:00Z", "issue": 1198, "notes": "kde/line_density return zeros for descending-y templates. Fix in PR #1199." },
|
|
16
|
+
"polygon_clip": { "last_inspected": "2026-04-13T12:00:00Z", "issue": 1197, "notes": "crop=True + all_touched=True drops boundary pixels. Fix in PR #1200." },
|
|
17
|
+
"resample": { "last_inspected": "2026-04-14T12:00:00Z", "issue": 1202, "notes": "Interpolation used edge-aligned coords instead of block-centered. Fix in PR #1204." },
|
|
18
|
+
"balanced_allocation": { "last_inspected": "2026-04-14T12:00:00Z", "issue": 1203, "notes": "float32 allocation array caused source ID mismatch for non-integer IDs. Fix in PR #1205." },
|
|
19
|
+
"dasymetric": { "last_inspected": "2026-04-14T12:00:00Z", "issue": null, "notes": "Mass conservation correct. Weighted/binary/limiting_variable all verified. Pycnophylactic Tobler algorithm correct." },
|
|
20
|
+
"geotiff": { "last_inspected": "2026-04-23", "issue": 1247, "severity_max": "HIGH", "categories_found": [1, 3, 4, 5], "notes": "HIGH fixed: CPU fp_predictor_decode wrong byte-lane layout for multi-sample predictor=3 (GPU was correct). MEDIUMs also fixed on same PR: eager and streaming writers emit LONG8 strip/tile offsets in BigTIFF output (were LONG); VRT read honors AREA_OR_POINT=Point; VRT nodata cast uses source dtype instead of float32. LOW fixed: duplicate LERC codec block removed from _compression.py." }
|
|
21
|
+
}
|
|
22
|
+
}
|