mapsmith 0.5.0__tar.gz → 0.6.0__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 (139) hide show
  1. {mapsmith-0.5.0 → mapsmith-0.6.0}/.zenodo.json +1 -1
  2. {mapsmith-0.5.0 → mapsmith-0.6.0}/CHANGELOG.md +174 -0
  3. {mapsmith-0.5.0 → mapsmith-0.6.0}/CITATION.cff +2 -2
  4. {mapsmith-0.5.0 → mapsmith-0.6.0}/PKG-INFO +181 -116
  5. {mapsmith-0.5.0 → mapsmith-0.6.0}/README.md +180 -115
  6. {mapsmith-0.5.0 → mapsmith-0.6.0}/SECURITY.md +13 -0
  7. {mapsmith-0.5.0 → mapsmith-0.6.0}/docs/catalog-entry-spec.md +6 -6
  8. {mapsmith-0.5.0 → mapsmith-0.6.0}/docs/manifest-vocabulary.md +13 -7
  9. {mapsmith-0.5.0 → mapsmith-0.6.0}/examples/01_verified_geoprocessing.ipynb +26 -26
  10. {mapsmith-0.5.0 → mapsmith-0.6.0}/examples/02_terrain_hydrology.ipynb +36 -36
  11. {mapsmith-0.5.0 → mapsmith-0.6.0}/examples/03_validated_plans.ipynb +24 -31
  12. {mapsmith-0.5.0 → mapsmith-0.6.0}/funding.json +1 -1
  13. {mapsmith-0.5.0 → mapsmith-0.6.0}/pyproject.toml +1 -1
  14. {mapsmith-0.5.0 → mapsmith-0.6.0}/server.json +3 -3
  15. {mapsmith-0.5.0 → mapsmith-0.6.0}/site/build.py +115 -0
  16. {mapsmith-0.5.0 → mapsmith-0.6.0}/site/index.template.html +142 -31
  17. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/__init__.py +1 -1
  18. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/antimeridian.py +125 -8
  19. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/catalog.py +56 -2
  20. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/engines/dispatch.py +24 -0
  21. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/engines/duckdb_engine.py +23 -20
  22. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/engines/raster.py +152 -130
  23. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/engines/sampling.py +12 -6
  24. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/engines/vector.py +598 -206
  25. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/engines/whitebox_engine.py +174 -139
  26. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/plans/registry.py +14 -0
  27. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/provenance.py +58 -39
  28. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/conftest.py +22 -1
  29. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/data/manifest-v1.schema.json +25 -3
  30. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/data/manifest_spec_validator.py +76 -38
  31. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_datum.py +276 -3
  32. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_discovery_report.py +149 -114
  33. mapsmith-0.6.0/tests/test_failure_manifest.py +247 -0
  34. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_lineage.py +4 -3
  35. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_raster_reproject_bands.py +176 -103
  36. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_showcase.py +270 -2
  37. mapsmith-0.6.0/tests/test_summarize_points_in_polygons.py +667 -0
  38. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_verify.py +52 -5
  39. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_whitebox_derivatives.py +41 -0
  40. {mapsmith-0.5.0 → mapsmith-0.6.0}/.dockerignore +0 -0
  41. {mapsmith-0.5.0 → mapsmith-0.6.0}/.gitignore +0 -0
  42. {mapsmith-0.5.0 → mapsmith-0.6.0}/CLAUDE.md +0 -0
  43. {mapsmith-0.5.0 → mapsmith-0.6.0}/CONTRIBUTING.md +0 -0
  44. {mapsmith-0.5.0 → mapsmith-0.6.0}/Dockerfile +0 -0
  45. {mapsmith-0.5.0 → mapsmith-0.6.0}/LICENSE +0 -0
  46. {mapsmith-0.5.0 → mapsmith-0.6.0}/MANIFESTO.md +0 -0
  47. {mapsmith-0.5.0 → mapsmith-0.6.0}/TRADEMARKS.md +0 -0
  48. {mapsmith-0.5.0 → mapsmith-0.6.0}/docker-compose.yml +0 -0
  49. {mapsmith-0.5.0 → mapsmith-0.6.0}/docs/argleton-run.json +0 -0
  50. {mapsmith-0.5.0 → mapsmith-0.6.0}/docs/benchmarks.md +0 -0
  51. {mapsmith-0.5.0 → mapsmith-0.6.0}/examples/README.md +0 -0
  52. {mapsmith-0.5.0 → mapsmith-0.6.0}/examples/fixtures/mount_st_helens_dem.tif +0 -0
  53. {mapsmith-0.5.0 → mapsmith-0.6.0}/glama.json +0 -0
  54. {mapsmith-0.5.0 → mapsmith-0.6.0}/schema/operation.schema.json +0 -0
  55. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/datum.py +0 -0
  56. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/discovery_log.py +0 -0
  57. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/engines/__init__.py +0 -0
  58. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/engines/esri.py +0 -0
  59. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/engines/geodesy.py +0 -0
  60. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/engines/linework.py +0 -0
  61. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/engines/network.py +0 -0
  62. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/engines/sedona_engine.py +0 -0
  63. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/engines/spatial_stats.py +0 -0
  64. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/engines/summaries.py +0 -0
  65. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/gdal_policy.py +0 -0
  66. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/grid.py +0 -0
  67. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/jobs.py +0 -0
  68. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/lineage.py +0 -0
  69. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/plans/__init__.py +0 -0
  70. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/plans/executor.py +0 -0
  71. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/plans/models.py +0 -0
  72. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/plans/validator.py +0 -0
  73. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/preview.py +0 -0
  74. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/readers.py +0 -0
  75. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/retrieval.py +0 -0
  76. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/server.py +0 -0
  77. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/sql_policy.py +0 -0
  78. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/stacks.py +0 -0
  79. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/ui.py +0 -0
  80. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/verify.py +0 -0
  81. {mapsmith-0.5.0 → mapsmith-0.6.0}/src/mapsmith/workspace.py +0 -0
  82. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/data/discovery_queries.json +0 -0
  83. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_ambiguous_layer.py +0 -0
  84. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_antimeridian.py +0 -0
  85. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_band_math.py +0 -0
  86. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_catalog.py +0 -0
  87. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_catalog_applicability.py +0 -0
  88. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_catalog_entry_spec.py +0 -0
  89. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_catalog_retrieval.py +0 -0
  90. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_centroid_convert.py +0 -0
  91. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_clip_reclassify_raster.py +0 -0
  92. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_contours_and_cost.py +0 -0
  93. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_dashboard.py +0 -0
  94. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_describe_raster.py +0 -0
  95. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_discovery_contract.py +0 -0
  96. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_discovery_log.py +0 -0
  97. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_duckdb_sandbox.py +0 -0
  98. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_engines.py +0 -0
  99. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_environment.py +0 -0
  100. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_gdal_policy.py +0 -0
  101. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_geodesy.py +0 -0
  102. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_geoparquet2.py +0 -0
  103. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_grid_registration.py +0 -0
  104. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_linework.py +0 -0
  105. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_measure_area.py +0 -0
  106. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_merge_simplify.py +0 -0
  107. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_nearest_explode.py +0 -0
  108. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_network.py +0 -0
  109. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_overlay_dissolve.py +0 -0
  110. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_path_containment.py +0 -0
  111. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_plans.py +0 -0
  112. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_preview.py +0 -0
  113. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_published_figures.py +0 -0
  114. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_raster.py +0 -0
  115. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_redaction.py +0 -0
  116. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_resample_raster.py +0 -0
  117. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_retrieval.py +0 -0
  118. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_retrieval_at_scale.py +0 -0
  119. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_retrieval_degradation.py +0 -0
  120. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_sampling.py +0 -0
  121. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_select_and_extract.py +0 -0
  122. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_smoke.py +0 -0
  123. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_south_up_and_mixed.py +0 -0
  124. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_spatial_stats.py +0 -0
  125. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_sql_axis_order.py +0 -0
  126. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_sql_credentials.py +0 -0
  127. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_stacks.py +0 -0
  128. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_summaries.py +0 -0
  129. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_terrain_derivatives.py +0 -0
  130. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_tier_a_operations.py +0 -0
  131. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_ui.py +0 -0
  132. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_verification_status.py +0 -0
  133. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_verify_repair.py +0 -0
  134. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_voronoi.py +0 -0
  135. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_whitebox.py +0 -0
  136. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_whitebox_encoding.py +0 -0
  137. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_whitebox_geographic_refusal.py +0 -0
  138. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_worked_example.py +0 -0
  139. {mapsmith-0.5.0 → mapsmith-0.6.0}/tests/test_workspace.py +0 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "upload_type": "software",
3
3
  "title": "MapSmith: geospatial processing over MCP, with verifiable provenance",
4
- "version": "0.5.0",
4
+ "version": "0.6.0",
5
5
  "creators": [
6
6
  {"name": "The MapSmith project"}
7
7
  ],
@@ -8,6 +8,180 @@ All notable changes to MapSmith are documented here, in the format of
8
8
 
9
9
  Nothing yet.
10
10
 
11
+ ## [0.6.0] - 2026-09-25
12
+
13
+ The reason to upgrade is in **Fixed**: five operations stop reading a zone drawn
14
+ across the 180th meridian as the rest of the planet, `nearest_join` stops
15
+ measuring distances there 5-6% too long, and 28 writers stop leaving a dataset
16
+ with no manifest when they fail. The reason to read before upgrading is in
17
+ **Changed**: one manifest key is renamed.
18
+
19
+ ### Added
20
+
21
+ - **Two cases on [mapsmith.dev](https://mapsmith.dev/#step-through) that a reader
22
+ steps through**, both executed while the page is built. One is a whole
23
+ analysis: the question in plain words, the catalogue narrowing with the counts
24
+ it produced, a plan refused before anything ran, five steps with their engine,
25
+ CRS decision and checks read from the manifests, the answer, and the chain
26
+ recovered from the final file alone. The other is a refusal — a GeoTIFF that
27
+ opens in every viewer, carrying its georeferencing twice and disagreeing with
28
+ itself, declined with a message naming both readings and how to choose.
29
+
30
+ Nothing in the installable product changed. The script is progressive: every
31
+ beat ships as ordinary HTML and the script only hides them and adds a button,
32
+ so the page reads in full with JavaScript off.
33
+
34
+ You pick the question, not the data, and that is a security decision rather
35
+ than a limitation: an upload box would mean running GDAL on files from
36
+ strangers, which is the assumption `SECURITY.md` is built on refusing.
37
+
38
+ - **`summarize_points_in_polygons`**: a numeric attribute of the points in each
39
+ polygon — count, sum, mean, median, min, max, stdev, range. "Average pH by
40
+ field block" used to take two operations, which to somebody searching is an
41
+ operation that does not exist. Three decisions about the number are in the
42
+ record: a polygon with no points is kept with its statistics null, because
43
+ the mean of no values is not zero; a point with no value is placed and not
44
+ summarised, so `<field>_count` can be smaller than `point_count` and says so;
45
+ and the boundary rule is the caller's, with the number of points it counted
46
+ twice or dropped. A check reads the output back and verifies the counts add
47
+ up to what the join found. Points only: a MultiPoint is refused rather than
48
+ carried whole into every polygon it touches, and a Shapefile output is
49
+ refused where the driver would truncate a column into the attribute's own
50
+ name. Reached through `run_operation`; the tool count is unchanged.
51
+
52
+ ### Changed
53
+
54
+ - **Records declare `spec_version` `1.0.0-draft.6`, and `x-mapsmith:round_trip`
55
+ is now `round_trip`.** Breaking for anyone who reads that key. The
56
+ specification gained a core key for "computed in another CRS and written back
57
+ in the caller's", with the two legs MapSmith already recorded, and draft.6
58
+ forbids recording that fact under a prefixed name: two producers naming the
59
+ same fact differently is two facts to a consumer. The shape is unchanged —
60
+ `transformation` for the way out, `return_transformation` for the way back —
61
+ so a reader needs to change the key it looks up and nothing else.
62
+
63
+ ### Fixed
64
+
65
+ - **`count_in_polygons(predicate="contains")` never placed a point, and said it
66
+ had verified.** `sjoin(points, polygons, predicate)` evaluates the predicate
67
+ as `predicate(point, polygon)`, so `contains` asked whether a point contains
68
+ a polygon — false for any polygon with area. Every count came back zero, the
69
+ record said `verified: true`, and the one check that noticed was
70
+ non-critical and blamed the points for lying outside every polygon. Even the
71
+ reading that was meant, the polygon containing the point, is exactly
72
+ `within`. `contains` is refused now, with the two predicates that mean
73
+ something for points; a caller using it was getting zeros, so nothing that
74
+ worked stops working. Found while building its sibling above, which had
75
+ inherited the same line.
76
+ - **`count_in_polygons` counted a MultiPoint once in every polygon it touched,
77
+ and blamed the polygons for points that had no position.** Both measured
78
+ before the fix: a MultiPoint straddling two squares came back as one point in
79
+ each, and a null and an empty geometry were reported as "2 of 3 points fall
80
+ in no polygon" with a hint about checking the boundaries. Multi-part and
81
+ areal geometries are refused now, with the way out; points without a
82
+ geometry are counted apart as `points_without_geometry` and no longer fail
83
+ the placement check.
84
+ - **A zone drawn across the 180th meridian as one ring counted the wrong
85
+ points, and the total could be right.** Every planar library reads such a
86
+ ring as its complement, the rest of the planet. Measured with two points, one
87
+ at 175°E inside the zone and one at 0°: the first was dropped, the second
88
+ counted, and the total was 1 — the same as the truth. Both point-in-polygon
89
+ operations now refuse that ring with the way out (split it at 180, as RFC
90
+ 7946 §3.1.9 prescribes, which then places correctly). The detector is in
91
+ `antimeridian.naive_crossings` and only looks at layers in degrees.
92
+ Measured the same way on the other region operations before extending it:
93
+ `clip_layer`, `spatial_join` and `overlay_layers` each returned the feature
94
+ on the far side of the world instead of the one inside the zone, and
95
+ `nearest_join` returned nothing; all four refuse that ring now, on either
96
+ input, and `spatial_join` on every engine — GeoParquet in one CRS is routed
97
+ to DuckDB or SedonaDB, which read the ring the same planar way. `measure_area`
98
+ is not affected — its geodesic area follows the edge the short way — and is
99
+ left alone.
100
+
101
+ What is **not** refused, because it is not the defect: an edge that runs
102
+ from -180 to 180 along the edge of the plane. That is how a rectangle
103
+ covering the world, a latitude band, a polar cap and Antarctica in the
104
+ Natural Earth countries file close their rings, and a first version of the
105
+ detector refused all four — so "points per country" would have been refused.
106
+ Found by the pre-release review, and the Natural Earth file is a test now.
107
+ - **`nearest_join` measured distances 5-6% too long across the antimeridian,
108
+ with no warning.** It works in an estimated UTM zone, and GeoPandas centres
109
+ that estimate on the mean of `minx` and `maxx`: for data straddling 180 that
110
+ is about 0, so two points at 175°E and 175°W were given EPSG:32630, centred on
111
+ 3°W. Their distances to a zone three degrees away came back as 351.7 and
112
+ 354.0 km where the truth is 334.0. `antimeridian.estimate_utm_crs` centres on
113
+ the true extent instead (here UTM 1N, within one per cent), and returns
114
+ exactly what GeoPandas does for data that `describe_dataset` does not report
115
+ as crossing. That definition is wider than touching 180: points at 100°E and
116
+ 170°W are closer going across it, and now get UTM 55N, centred on 147°E,
117
+ where GeoPandas gave 25N, centred on 33°W. A layer split at 180 as a single MultiPolygon, the
118
+ form the refusal above asks for, is read part by part and counts as crossing.
119
+ `buffer_layer`, `simplify_layer` and `centroid_layer` use it too; a 1 km
120
+ buffer had measured correctly even in the far zone, so for them it is the
121
+ right zone rather than a fix.
122
+
123
+ - **Five operations wrote a fact into the manifest before it happened.** The
124
+ record survives a crash, which is the point of it, and on these paths it
125
+ survived carrying a sentence that had become false.
126
+ - `simplify_layer`, `centroid_layer` and `nearest_join` recorded the round
127
+ trip before the way back ran, and the way back runs inside the block that
128
+ writes the record on failure. A return leg that raised left a manifest
129
+ asserting a trip that never finished. `buffer_layer` had the right order
130
+ by accident. The trip is recorded only once the output is home, and
131
+ draft.6 now requires exactly that of every producer.
132
+ - `reproject_layer` and `reproject_raster` recorded `target_crs` and the
133
+ transformation before any coordinate moved, so a failed reprojection said
134
+ where the data had been put and by which operation — with a seven-metre
135
+ datum shift, on a NAD27 input, beside geometry that had not gone anywhere.
136
+ - Three `reason` strings said "output geometries are returned in the input
137
+ CRS", written before the work and surviving on the failing path while the
138
+ key correctly disappeared. The key says it now, when it is true.
139
+ - **`reproject_raster` could leave a raster with no manifest beside it.** A
140
+ warp that raised on the second of three bands left a 19608-byte file on disk
141
+ — band one warped, two and three zero-filled — and nothing describing it.
142
+ The write is inside the failure audit now, and a partial file carries a
143
+ record that says `x-mapsmith:operation_completed: false`.
144
+ - **The same defect was in 28 of the 58 writers 0.5.1 shipped, found by
145
+ looking for it everywhere instead of where it had last been seen.** A write
146
+ that died halfway, or a check that raised after the output reached the disk,
147
+ left the dataset with nothing beside it: in `resample_raster` — the twin of
148
+ the operation above — and in the thirteen Whitebox operations, `run_sql`,
149
+ five more raster operations (`reproject_raster` among them, whose write was
150
+ covered and whose reading-back was not), both sampling operations and six
151
+ vector ones. Every writer now holds the net from its first byte to its
152
+ manifest, and `tests/test_failure_manifest.py` injects both failures into
153
+ every writing operation the catalogue lists. Run against the code just
154
+ before this change, it fails on those 28 and on one more,
155
+ `summarize_points_in_polygons`, which had inherited the shape before it ever
156
+ shipped — and on no other.
157
+
158
+ ### Security
159
+
160
+ - **No workflow job that runs this repository's code can mint an OIDC token.**
161
+ The MCP Registry grants `io.github.mapsmith-ai/*` to a token from any
162
+ workflow of this owner. The site workflow granted `id-token: write` to all
163
+ its jobs, including the one that runs `pip install` and our own build
164
+ scripts; only the deploy job, which runs one pinned action, holds it now.
165
+ The new registry workflow publishes a tag only if its commit is on `main` and
166
+ its own CI run passed — a dry run on `v0.5.0`, whose CI failed, stops there.
167
+
168
+ ## [0.5.1] - 2026-09-21
169
+
170
+ ### Fixed
171
+
172
+ - **0.5.0 is yanked on PyPI, and the reason is its own page.** The release
173
+ procedure builds and uploads only after the tag's CI is green, because that
174
+ run is the one that checks the README against the release it claims to
175
+ describe. The upload went first: PyPI took the sdist whose front page says
176
+ *"This page describes 0.4.0"* and *"`main` is ahead of 0.4.0 today"*, and a
177
+ PyPI page cannot be edited afterwards. The code in 0.5.0 is the code here;
178
+ what is wrong is the artefact describing itself as the previous version, on
179
+ a product whose argument is that records describe what they say they do.
180
+ 0.5.1 is 0.5.0 with that paragraph correct.
181
+
182
+ Nothing else moved: records still declare `spec_version` `1.0.0-draft.5`, the
183
+ tool surface is the same, and the entries below are this release's.
184
+
11
185
  ## [0.5.0] - 2026-09-21
12
186
 
13
187
  Everything here was on `main` and not in a release. It is almost entirely
@@ -12,8 +12,8 @@ identifiers:
12
12
  value: "10.5281/zenodo.22213091"
13
13
  description: "Concept DOI — always resolves to the latest release"
14
14
  license: AGPL-3.0-or-later
15
- version: "0.5.0"
16
- date-released: "2026-09-21"
15
+ version: "0.6.0"
16
+ date-released: "2026-09-25"
17
17
  keywords:
18
18
  - GIS
19
19
  - geospatial