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.
Files changed (324) hide show
  1. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/rockout.md +5 -13
  2. xarray_spatial-0.9.7/.claude/commands/sweep-accuracy.md +257 -0
  3. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/sweep-performance.md +25 -17
  4. xarray_spatial-0.9.7/.claude/commands/sweep-security.md +253 -0
  5. xarray_spatial-0.9.7/.claude/sweep-accuracy-state.json +22 -0
  6. xarray_spatial-0.9.7/.claude/sweep-performance-state.json +50 -0
  7. xarray_spatial-0.9.7/.claude/sweep-security-state.json +108 -0
  8. xarray_spatial-0.9.7/.efficiency-audit-baseline.json +171 -0
  9. xarray_spatial-0.9.7/.efficiency-audit-baseline.prev.json +138 -0
  10. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.github/ISSUE_TEMPLATE/feature-proposal.md +0 -1
  11. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.gitignore +1 -0
  12. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/CHANGELOG.md +47 -0
  13. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/PKG-INFO +19 -3
  14. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/README.md +18 -2
  15. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xarray_spatial.egg-info/PKG-INFO +19 -3
  16. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xarray_spatial.egg-info/SOURCES.txt +17 -3
  17. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/__init__.py +5 -0
  18. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/_version.py +3 -3
  19. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/accessor.py +9 -1
  20. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/balanced_allocation.py +2 -2
  21. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/bilateral.py +13 -0
  22. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/bump.py +141 -49
  23. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/classify.py +127 -43
  24. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/contour.py +31 -0
  25. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/convolution.py +47 -0
  26. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/cost_distance.py +1 -1
  27. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/__init__.py +160 -42
  28. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/_compression.py +0 -55
  29. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/_gpu_decode.py +99 -8
  30. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/_header.py +64 -0
  31. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/_reader.py +99 -13
  32. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/_vrt.py +41 -3
  33. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/_writer.py +60 -11
  34. xarray_spatial-0.9.7/xrspatial/geotiff/tests/test_cog.py +379 -0
  35. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_features.py +229 -2
  36. xarray_spatial-0.9.7/xrspatial/geotiff/tests/test_predictor_multisample.py +398 -0
  37. xarray_spatial-0.9.7/xrspatial/geotiff/tests/test_security.py +519 -0
  38. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hillshade.py +56 -25
  39. xarray_spatial-0.9.7/xrspatial/kde.py +747 -0
  40. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/pathfinding.py +6 -2
  41. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/perlin.py +10 -0
  42. xarray_spatial-0.9.7/xrspatial/polygon_clip.py +272 -0
  43. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/polygonize.py +560 -5
  44. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/rasterize.py +37 -0
  45. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/__init__.py +45 -22
  46. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_grid.py +11 -0
  47. xarray_spatial-0.9.7/xrspatial/resample.py +791 -0
  48. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/sieve.py +126 -96
  49. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_balanced_allocation.py +39 -0
  50. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_bilateral.py +40 -0
  51. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_bump.py +130 -0
  52. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_classify.py +44 -4
  53. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_contour.py +38 -0
  54. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_cost_distance.py +41 -0
  55. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_focal.py +24 -0
  56. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_hillshade.py +73 -20
  57. xarray_spatial-0.9.7/xrspatial/tests/test_hypsometric_integral.py +256 -0
  58. xarray_spatial-0.9.7/xrspatial/tests/test_kde.py +461 -0
  59. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_perlin.py +27 -0
  60. xarray_spatial-0.9.7/xrspatial/tests/test_polygon_clip.py +360 -0
  61. xarray_spatial-0.9.7/xrspatial/tests/test_polygonize.py +1070 -0
  62. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_rasterize.py +49 -0
  63. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_reproject.py +59 -0
  64. xarray_spatial-0.9.7/xrspatial/tests/test_resample.py +380 -0
  65. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_sieve.py +18 -22
  66. xarray_spatial-0.9.7/xrspatial/tests/test_sieve_gdal_parity.py +467 -0
  67. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_viewshed.py +42 -0
  68. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_visibility.py +14 -11
  69. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_zonal.py +39 -0
  70. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/viewshed.py +20 -2
  71. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/visibility.py +114 -56
  72. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/zonal.py +203 -1
  73. xarray_spatial-0.9.6/.claude/accuracy-sweep-state.json +0 -9
  74. xarray_spatial-0.9.6/.claude/commands/accuracy-sweep.md +0 -158
  75. xarray_spatial-0.9.6/.claude/performance-sweep-state.json +0 -47
  76. xarray_spatial-0.9.6/xrspatial/geotiff/tests/test_cog.py +0 -137
  77. xarray_spatial-0.9.6/xrspatial/tests/test_polygonize.py +0 -540
  78. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/backend-parity.md +0 -0
  79. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/bench.md +0 -0
  80. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/dask-notebook.md +0 -0
  81. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/efficiency-audit.md +0 -0
  82. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/new-issues.md +0 -0
  83. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/release-major.md +0 -0
  84. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/release-minor.md +0 -0
  85. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/release-patch.md +0 -0
  86. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/review-pr.md +0 -0
  87. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/user-guide-notebook.md +0 -0
  88. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.claude/commands/validate.md +0 -0
  89. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.gitattributes +0 -0
  90. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  91. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.github/labeler.yml +0 -0
  92. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.github/pull_request_template.md +0 -0
  93. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.github/workflows/benchmarks.yml +0 -0
  94. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.github/workflows/labeler.yml +0 -0
  95. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.github/workflows/pypi-publish.yml +0 -0
  96. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.github/workflows/test.yml +0 -0
  97. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/.readthedocs.yml +0 -0
  98. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/CODE_OF_CONDUCT.md +0 -0
  99. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/CONTRIBUTING.md +0 -0
  100. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/Citation-styles.md +0 -0
  101. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/LICENSE.txt +0 -0
  102. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/MANIFEST.in +0 -0
  103. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/RELEASE.md +0 -0
  104. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/codecov.yml +0 -0
  105. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/pyproject.toml +0 -0
  106. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/setup.cfg +0 -0
  107. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/setup.py +0 -0
  108. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xarray_spatial.egg-info/dependency_links.txt +0 -0
  109. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xarray_spatial.egg-info/entry_points.txt +0 -0
  110. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xarray_spatial.egg-info/not-zip-safe +0 -0
  111. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xarray_spatial.egg-info/requires.txt +0 -0
  112. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xarray_spatial.egg-info/top_level.txt +0 -0
  113. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/__main__.py +0 -0
  114. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/analytics.py +0 -0
  115. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/aspect.py +0 -0
  116. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/corridor.py +0 -0
  117. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/curvature.py +0 -0
  118. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/dasymetric.py +0 -0
  119. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/dataset_support.py +0 -0
  120. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/datasets/__init__.py +0 -0
  121. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/datasets/sentinel-2/blue_band.nc +0 -0
  122. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/datasets/sentinel-2/green_band.nc +0 -0
  123. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/datasets/sentinel-2/nir_band.nc +0 -0
  124. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/datasets/sentinel-2/red_band.nc +0 -0
  125. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/datasets/sentinel-2/swir1_band.nc +0 -0
  126. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/datasets/sentinel-2/swir2_band.nc +0 -0
  127. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/diagnostics.py +0 -0
  128. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/diffusion.py +0 -0
  129. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/edge_detection.py +0 -0
  130. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/emerging_hotspots.py +0 -0
  131. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/erosion.py +0 -0
  132. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/experimental/__init__.py +0 -0
  133. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/experimental/min_observable_height.py +0 -0
  134. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/fire.py +0 -0
  135. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/flood.py +0 -0
  136. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/focal.py +0 -0
  137. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geodesic.py +0 -0
  138. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/_dtypes.py +0 -0
  139. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/_geotags.py +0 -0
  140. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/__init__.py +0 -0
  141. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/bench_vs_rioxarray.py +0 -0
  142. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/conftest.py +0 -0
  143. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_accessor_io.py +0 -0
  144. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_accuracy_1081.py +0 -0
  145. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_compression.py +0 -0
  146. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_compression_level.py +0 -0
  147. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_dtype_read.py +0 -0
  148. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_edge_cases.py +0 -0
  149. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_geotags.py +0 -0
  150. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_header.py +0 -0
  151. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_jpeg.py +0 -0
  152. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_jpeg2000.py +0 -0
  153. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_lerc.py +0 -0
  154. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_lz4.py +0 -0
  155. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_reader.py +0 -0
  156. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_streaming_write.py +0 -0
  157. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_vrt_write.py +0 -0
  158. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/geotiff/tests/test_writer.py +0 -0
  159. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/glcm.py +0 -0
  160. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/gpu_rtx/__init__.py +0 -0
  161. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/gpu_rtx/cuda_utils.py +0 -0
  162. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/gpu_rtx/hillshade.py +0 -0
  163. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/gpu_rtx/mesh_utils.py +0 -0
  164. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/gpu_rtx/viewshed.py +0 -0
  165. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/__init__.py +0 -0
  166. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/_boundary_store.py +0 -0
  167. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/basin_d8.py +0 -0
  168. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/fill_d8.py +0 -0
  169. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_accumulation_d8.py +0 -0
  170. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_accumulation_dinf.py +0 -0
  171. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_accumulation_mfd.py +0 -0
  172. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_direction_d8.py +0 -0
  173. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_direction_dinf.py +0 -0
  174. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_direction_mfd.py +0 -0
  175. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_length_d8.py +0 -0
  176. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_length_dinf.py +0 -0
  177. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_length_mfd.py +0 -0
  178. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_path_d8.py +0 -0
  179. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_path_dinf.py +0 -0
  180. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/flow_path_mfd.py +0 -0
  181. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/hand_d8.py +0 -0
  182. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/hand_dinf.py +0 -0
  183. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/hand_mfd.py +0 -0
  184. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/sink_d8.py +0 -0
  185. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/snap_pour_point_d8.py +0 -0
  186. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/stream_link_d8.py +0 -0
  187. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/stream_link_dinf.py +0 -0
  188. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/stream_link_mfd.py +0 -0
  189. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/stream_order_d8.py +0 -0
  190. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/stream_order_dinf.py +0 -0
  191. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/stream_order_mfd.py +0 -0
  192. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/__init__.py +0 -0
  193. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/conftest.py +0 -0
  194. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_basin_d8.py +0 -0
  195. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_fill_d8.py +0 -0
  196. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_accumulation_d8.py +0 -0
  197. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_accumulation_dinf.py +0 -0
  198. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_accumulation_mfd.py +0 -0
  199. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_direction_d8.py +0 -0
  200. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_direction_dinf.py +0 -0
  201. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_direction_mfd.py +0 -0
  202. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_length_d8.py +0 -0
  203. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_length_dinf.py +0 -0
  204. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_length_mfd.py +0 -0
  205. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_path_d8.py +0 -0
  206. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_path_dinf.py +0 -0
  207. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_flow_path_mfd.py +0 -0
  208. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_hand_d8.py +0 -0
  209. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_hand_dinf.py +0 -0
  210. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_hand_mfd.py +0 -0
  211. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_sink_d8.py +0 -0
  212. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_snap_pour_point_d8.py +0 -0
  213. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_stream_link_d8.py +0 -0
  214. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_stream_link_dinf.py +0 -0
  215. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_stream_link_mfd.py +0 -0
  216. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_stream_order_d8.py +0 -0
  217. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_stream_order_dinf.py +0 -0
  218. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_stream_order_mfd.py +0 -0
  219. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_twi_d8.py +0 -0
  220. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_watershed_d8.py +0 -0
  221. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_watershed_dinf.py +0 -0
  222. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/tests/test_watershed_mfd.py +0 -0
  223. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/twi_d8.py +0 -0
  224. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/watershed_d8.py +0 -0
  225. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/watershed_dinf.py +0 -0
  226. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/hydro/watershed_mfd.py +0 -0
  227. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/interpolate/__init__.py +0 -0
  228. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/interpolate/_idw.py +0 -0
  229. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/interpolate/_kriging.py +0 -0
  230. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/interpolate/_spline.py +0 -0
  231. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/interpolate/_validation.py +0 -0
  232. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/mahalanobis.py +0 -0
  233. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/mcda/__init__.py +0 -0
  234. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/mcda/combine.py +0 -0
  235. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/mcda/constrain.py +0 -0
  236. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/mcda/sensitivity.py +0 -0
  237. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/mcda/standardize.py +0 -0
  238. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/mcda/weights.py +0 -0
  239. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/morphology.py +0 -0
  240. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/multispectral.py +0 -0
  241. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/normalize.py +0 -0
  242. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/preview.py +0 -0
  243. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/proximity.py +0 -0
  244. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_crs_utils.py +0 -0
  245. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_datum_grids.py +0 -0
  246. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_interpolate.py +0 -0
  247. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_itrf.py +0 -0
  248. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_lite_crs.py +0 -0
  249. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_merge.py +0 -0
  250. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_projections.py +0 -0
  251. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_projections_cuda.py +0 -0
  252. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_transform.py +0 -0
  253. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/_vertical.py +0 -0
  254. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/at_bev_AT_GIS_GRID.tif +0 -0
  255. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/au_icsm_A66_National_13_09_01.tif +0 -0
  256. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/be_ign_bd72lb72_etrs89lb08.tif +0 -0
  257. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/ch_swisstopo_CHENyx06_ETRS.tif +0 -0
  258. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/de_adv_BETA2007.tif +0 -0
  259. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/es_ign_SPED2ETV2.tif +0 -0
  260. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/nl_nsgi_rdcorr2018.tif +0 -0
  261. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/pt_dgt_D73_ETRS89_geo.tif +0 -0
  262. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/uk_os_OSTN15_NTv2_OSGBtoETRS.tif +0 -0
  263. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/us_nga_egm96_15.tif +0 -0
  264. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/us_noaa_alaska.tif +0 -0
  265. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/us_noaa_conus.tif +0 -0
  266. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/us_noaa_hawaii.tif +0 -0
  267. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/us_noaa_nadcon5_nad27_nad83_1986_conus.tif +0 -0
  268. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/reproject/grids/us_noaa_prvi.tif +0 -0
  269. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/sky_view_factor.py +0 -0
  270. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/slope.py +0 -0
  271. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/surface_distance.py +0 -0
  272. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/terrain.py +0 -0
  273. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/terrain_metrics.py +0 -0
  274. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/__init__.py +0 -0
  275. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/bench_reproject_vs_rioxarray.py +0 -0
  276. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/conftest.py +0 -0
  277. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/general_checks.py +0 -0
  278. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_accessor.py +0 -0
  279. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_analytics.py +0 -0
  280. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_aspect.py +0 -0
  281. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_corridor.py +0 -0
  282. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_curvature.py +0 -0
  283. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_dask_cupy_gaps.py +0 -0
  284. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_dask_laziness.py +0 -0
  285. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_dasymetric.py +0 -0
  286. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_dataset_support.py +0 -0
  287. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_datasets.py +0 -0
  288. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_diagnostics.py +0 -0
  289. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_diffusion.py +0 -0
  290. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_edge_detection.py +0 -0
  291. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_emerging_hotspots.py +0 -0
  292. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_erosion.py +0 -0
  293. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_fire.py +0 -0
  294. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_flood.py +0 -0
  295. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_fused_overlap.py +0 -0
  296. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_geodesic_aspect.py +0 -0
  297. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_geodesic_slope.py +0 -0
  298. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_glcm.py +0 -0
  299. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_glcm_metric_order.py +0 -0
  300. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_interpolation.py +0 -0
  301. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_lite_crs.py +0 -0
  302. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_mahalanobis.py +0 -0
  303. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_mcda.py +0 -0
  304. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_min_observable_height.py +0 -0
  305. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_morphology.py +0 -0
  306. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_morphology_derived.py +0 -0
  307. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_multi_overlap.py +0 -0
  308. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_multispectral.py +0 -0
  309. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_normalize.py +0 -0
  310. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_northness_eastness.py +0 -0
  311. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_pathfinding.py +0 -0
  312. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_preview.py +0 -0
  313. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_proximity.py +0 -0
  314. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_rasterize_accuracy.py +0 -0
  315. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_rechunk_no_shuffle.py +0 -0
  316. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_sky_view_factor.py +0 -0
  317. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_slope.py +0 -0
  318. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_surface_distance.py +0 -0
  319. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_terrain.py +0 -0
  320. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_terrain_metrics.py +0 -0
  321. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_utils.py +0 -0
  322. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/tests/test_validation.py +0 -0
  323. {xarray_spatial-0.9.6 → xarray_spatial-0.9.7}/xrspatial/utils.py +0 -0
  324. {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-sweep-state.json` and treat all modules as never-inspected |
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-sweep-state.json`. If it does not exist, treat every
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-sweep-state.json` with the triage results:
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. Print the ralph-loop command
398
+ ### 6b. Invoke the ralph-loop automatically
399
399
 
400
- Using the target list, generate and print:
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
- /ralph-loop "Performance sweep Phase 2: benchmark and fix HIGH-severity findings.
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-sweep-state.json with the issue number.
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
- Set `--max-iterations` to the number of target modules plus 2 (buffer for
452
- retries).
461
+ ### 6c. Print status
453
462
 
454
- ### 6c. Print reminder text
463
+ After invoking the ralph-loop, print:
455
464
 
456
465
  ```
457
- Phase 1 triage complete. To proceed with fixes:
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-sweep-state.json`) is gitignored by
487
- convention do not add it to git.
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
+ }