openmapstack 0.2.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 (40) hide show
  1. openmapstack-0.2.0/LICENSE +21 -0
  2. openmapstack-0.2.0/PKG-INFO +268 -0
  3. openmapstack-0.2.0/README.md +251 -0
  4. openmapstack-0.2.0/openmapstack/__init__.py +6 -0
  5. openmapstack-0.2.0/openmapstack/__main__.py +3 -0
  6. openmapstack-0.2.0/openmapstack/checks/__init__.py +112 -0
  7. openmapstack-0.2.0/openmapstack/checks/geodata.py +318 -0
  8. openmapstack-0.2.0/openmapstack/checks/overrides.py +263 -0
  9. openmapstack-0.2.0/openmapstack/checks/presentation.py +168 -0
  10. openmapstack-0.2.0/openmapstack/checks/project.py +261 -0
  11. openmapstack-0.2.0/openmapstack/checks/provenance.py +133 -0
  12. openmapstack-0.2.0/openmapstack/checks/qgis.py +832 -0
  13. openmapstack-0.2.0/openmapstack/checks/rerun.py +340 -0
  14. openmapstack-0.2.0/openmapstack/checks/spatial.py +65 -0
  15. openmapstack-0.2.0/openmapstack/checks/validation.py +288 -0
  16. openmapstack-0.2.0/openmapstack/checks/visual.py +642 -0
  17. openmapstack-0.2.0/openmapstack/cli.py +431 -0
  18. openmapstack-0.2.0/openmapstack/expectations.py +284 -0
  19. openmapstack-0.2.0/openmapstack/integrity.py +137 -0
  20. openmapstack-0.2.0/openmapstack/project.py +79 -0
  21. openmapstack-0.2.0/openmapstack/rerun.py +332 -0
  22. openmapstack-0.2.0/openmapstack/schema.py +39 -0
  23. openmapstack-0.2.0/openmapstack/schemas/__init__.py +1 -0
  24. openmapstack-0.2.0/openmapstack/schemas/project-v1.schema.json +264 -0
  25. openmapstack-0.2.0/openmapstack/validation.py +1019 -0
  26. openmapstack-0.2.0/openmapstack/verify.py +386 -0
  27. openmapstack-0.2.0/openmapstack.egg-info/PKG-INFO +268 -0
  28. openmapstack-0.2.0/openmapstack.egg-info/SOURCES.txt +38 -0
  29. openmapstack-0.2.0/openmapstack.egg-info/dependency_links.txt +1 -0
  30. openmapstack-0.2.0/openmapstack.egg-info/entry_points.txt +2 -0
  31. openmapstack-0.2.0/openmapstack.egg-info/requires.txt +11 -0
  32. openmapstack-0.2.0/openmapstack.egg-info/top_level.txt +1 -0
  33. openmapstack-0.2.0/pyproject.toml +52 -0
  34. openmapstack-0.2.0/setup.cfg +4 -0
  35. openmapstack-0.2.0/tests/test_cli.py +565 -0
  36. openmapstack-0.2.0/tests/test_evals.py +996 -0
  37. openmapstack-0.2.0/tests/test_expectations.py +225 -0
  38. openmapstack-0.2.0/tests/test_release.py +28 -0
  39. openmapstack-0.2.0/tests/test_rerun.py +330 -0
  40. openmapstack-0.2.0/tests/test_verify.py +291 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jaak Laineste
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,268 @@
1
+ Metadata-Version: 2.4
2
+ Name: openmapstack
3
+ Version: 0.2.0
4
+ Summary: Validate, run, and inspect reproducible OpenMapStack projects
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Requires-Dist: jsonschema<5,>=4
9
+ Requires-Dist: PyYAML<7,>=6
10
+ Provides-Extra: geo
11
+ Requires-Dist: duckdb>=1.2; extra == "geo"
12
+ Provides-Extra: visual
13
+ Requires-Dist: playwright>=1.40; extra == "visual"
14
+ Provides-Extra: all
15
+ Requires-Dist: openmapstack[geo,visual]; extra == "all"
16
+ Dynamic: license-file
17
+
18
+ # openmapstack
19
+
20
+ **Geospatial questions → reproducible, validated GIS analysis project (with very nice interactive map).**
21
+
22
+ Install:
23
+ ```bash
24
+ npx skills add jaakla/openmapstack -g
25
+ ```
26
+
27
+ OpenMapStack gives your favorite AI agent: Claude Code, Codex, Cursor, OpenCode, and 50+ other agents a production workflow from **authoritative data discovery** through analysis to interactive web and GIS deliverables. Material workflows become inspectable and repeatable well-defined projects in a `yaml` file with pinned sources, explicit assumptions and CRS choices, deterministic processing, isolated overrides, machine-readable validation, and surfaced provenance.
28
+
29
+ It is open-first and cloud-native by default, built on shoulders of the awesome Open GIS stack: STAC for discovery; GeoParquet, COG, and PMTiles for storage and delivery; DuckDB and PostGIS for compute; and QGIS, MapLibre, and Martin for presentation. It also uses GDAL/OGR, GeoPandas, xarray/rioxarray, PDAL, routing engines, spatial SQL, and pragmatic hosted services when scale or reliability requires them.
30
+
31
+ ## What's in this repo
32
+
33
+ - [SKILL.md](SKILL.md) — the skill entry point: triggers, global defaults, format and compute decision matrices, anti-patterns, and a quick triage guide.
34
+ - [references/data-sources.md](references/data-sources.md) - lists OSM, Overture, Sentinel/Landsat, regional portals, STAC catalogs and others.
35
+ - [references/services-and-scale.md](references/services-and-scale.md) - depending on case use local installs or hosted/SaaS services for global-scale basemaps, elevation, routing, geocoding, place search, and postcodes.
36
+ - [references/formats-and-crs.md](references/formats-and-crs.md) - how to choose formats, conversions, projections, EPSG codes.
37
+ - [references/processing.md](references/processing.md) - when and how to use GDAL/OGR, GeoPandas, xarray, DuckDB, PostGIS, PDAL and other open geo processing tools.
38
+ - [references/analytics.md](references/analytics.md) — do vector/raster analytics, terrain, hydrology, network, point clouds, geocoding etc.
39
+ - [references/web-delivery.md](references/web-delivery.md) — renderer selection for maps, PMTiles, MVT, Martin, TiTiler, MapLibre, deck.gl, kepler.gl, and lonboard formats and engines.
40
+ - [references/qgis.md](references/qgis.md) — QGIS desktop, plugins, PyQGIS, Processing, QGIS MCP.
41
+ - [references/validation-and-ops.md](references/validation-and-ops.md) — validation, manifests, attribution, and deployment checks, including the machine-readable reproducible-project contract.
42
+ - [references/project-spec.md](references/project-spec.md) — the specific`openmapstack-project/v1` schema: compiling any material analysis into a reproducible GIS project (`project.yaml`, pipeline, source provenance, overrides, validation, semantic presentation, QGIS output).
43
+ - [templates/](templates/) — ready scaffolds (`project.yaml`, `pipeline.py`, `presentation.yaml`, `validation.yaml`) for new projects.
44
+ - [examples/tartu-development/](examples/tartu-development/) — a fully-worked reproducible project matching the acceptance scenario: source provenance + timestamps, explicit assumptions, two verified project overrides (a scenario attribute change with prior-value verification, and hypothetical scenario geometry), deterministic pipeline, machine-readable validation, and semantic presentation.
45
+ - [evals/](evals/) — the eval suite grading whether an agent reaches the right analytical answer, respects the GIS-method guardrails, and reruns reproducibly, with the `openmapstack-project/v1` contract as the substrate that makes those independently checkable: `python evals/run.py --mode fixture` runs deterministic, no-LLM checks against real generated artifacts (analytical correctness against known geospatial truth, metric CRS, source immutability, schema, overrides, validation integrity, presentation contract, and clean reruns), plus adversarial cases and a pluggable live-agent benchmark (Claude Code, Codex, and any OpenAI-compatible API such as OpenRouter — URL and model via `OPENAI_COMPATIBLE_*` env, API key as a secret).
46
+ - [`openmapstack/`](openmapstack/) — the installable `openmapstack validate/run/inspect` CLI for auditing and executing `openmapstack-project/v1` projects, plus [`openmapstack/checks/`](openmapstack/checks/): the reusable, semantic check library. All but five of its checks are oracle-free, so the same functions that grade the eval suite also grade a user's own project on data this repository has never seen.
47
+ - [`.claude-plugin/`](.claude-plugin/) — Claude Code plugin and marketplace manifests, so the repository can also be installed with `/plugin install`. Validated in CI by [`.github/workflows/plugin.yml`](.github/workflows/plugin.yml).
48
+
49
+ My local Estonia-specific guidance (Maa- ja Ruumiamet, ETAK, EPSG:3301 / L-EST97) is included for convenience. But all the global sources are incuded for world-wide coverage.
50
+
51
+ ## Install
52
+
53
+ The recommended way is the [skills CLI](https://github.com/vercel-labs/skills), which works for Claude Code, Cursor, OpenCode, Codex, and 50+ other agents.
54
+
55
+ ### Recommended: skills CLI
56
+
57
+ Install globally (available in every project):
58
+
59
+ ```bash
60
+ npx skills add jaakla/openmapstack -g
61
+ ```
62
+
63
+ Update later with `npx skills update open-map-stack`. Remove with `npx skills remove open-map-stack`.
64
+
65
+ ### Claude Code plugin (optional)
66
+
67
+ Claude Code users can install the same repository as a plugin instead. This adds
68
+ versioned installs, `/plugin update`, and project-scoped installs that a team
69
+ picks up from a repository's `.claude/settings.json`:
70
+
71
+ ```bash
72
+ /plugin marketplace add jaakla/openmapstack
73
+ /plugin install open-map-stack@open-map-stack
74
+ ```
75
+
76
+ The repository is its own marketplace, so no separate marketplace repo is
77
+ needed. The plugin wraps the same root `SKILL.md` — nothing is duplicated, and
78
+ the skills-CLI install path above keeps working unchanged.
79
+
80
+ ### Install the project CLI
81
+
82
+ The skills installer loads the agent instructions; the Python package provides
83
+ the project commands. From a clone of this repository:
84
+
85
+ ```bash
86
+ python3 -m pip install .
87
+ openmapstack --version
88
+ ```
89
+
90
+ For development, the commands can also run directly without installation:
91
+
92
+ ```bash
93
+ python3 -m openmapstack --help
94
+ ```
95
+
96
+ ### Manual install (fallback)
97
+
98
+ If you'd rather not use the CLI, clone directly into your agent's skills directory. For Claude Code:
99
+
100
+ ```bash
101
+ # User-level (every project)
102
+ git clone https://github.com/jaakla/openmapstack.git ~/.claude/skills/open-map-stack
103
+
104
+ # Project-level (one repo)
105
+ git clone https://github.com/jaakla/openmapstack.git .claude/skills/open-map-stack
106
+ ```
107
+
108
+ ### Verify
109
+
110
+ Start Claude Code and run `/skills open-map-stack` should appear in the list. The expected layout is:
111
+
112
+ ```
113
+ <skills-dir>/open-map-stack/
114
+ ├── SKILL.md
115
+ ├── references/
116
+ │ ├── analytics.md
117
+ │ ├── data-sources.md
118
+ │ ├── formats-and-crs.md
119
+ │ ├── processing.md
120
+ │ ├── project-spec.md
121
+ │ ├── qgis.md
122
+ │ ├── services-and-scale.md
123
+ │ ├── spatial-sql.md
124
+ │ ├── validation-and-ops.md
125
+ │ └── web-delivery.md
126
+ ├── templates/
127
+ │ ├── project.yaml
128
+ │ ├── pipeline.py
129
+ │ ├── presentation.yaml
130
+ │ └── validation.yaml
131
+ ├── examples/
132
+ │ └── tartu-development/
133
+ └── .claude-plugin/ # Claude Code plugin + marketplace manifests
134
+ ├── plugin.json
135
+ └── marketplace.json
136
+ ```
137
+
138
+ ## Use
139
+
140
+ The skill auto-activates when you ask Claude about geospatial work — terms like GIS, OpenStreetMap, Overture, Sentinel, Landsat, LiDAR, GeoTIFF, shapefile, GeoPackage, raster/vector tiles, isochrones, spatial joins, EPSG codes, and projections will all trigger it. You don't need to invoke it manually, but sometimes hinting "use open-map-stack skills" helps.
141
+
142
+ Example prompts that engage the skill:
143
+
144
+ - "Pull all buildings in Tartu from Overture and publish them as a PMTiles layer."
145
+ - "Compute average NDVI for these polygons from Sentinel-2 over the last 12 months."
146
+ - "Reproject this GeoTIFF from EPSG:3301 to EPSG:3857 as a COG."
147
+ - "Set up an OSRM routing server from a Estonia OSM extract."
148
+ - "Build an isochrone API around these points."
149
+
150
+ If you want to force the skill to load, you can reference it explicitly:
151
+
152
+ > Use the open-map-stack skill to convert this shapefile to GeoParquet.
153
+
154
+ ## Project CLI
155
+
156
+ The CLI operates on an `openmapstack-project/v1` manifest. A project directory may
157
+ be supplied in place of its `project.yaml` file.
158
+
159
+ ```bash
160
+ # Audit the complete artifact, including outputs, report, and run record.
161
+ openmapstack validate path/to/project.yaml
162
+
163
+ # Check the produced artifacts without requiring a golden answer.
164
+ openmapstack verify path/to/project.yaml
165
+
166
+ # Run the one canonical pipeline, then validate what it produced.
167
+ openmapstack run path/to/project.yaml
168
+
169
+ # Review sources, versions, overrides, ordered steps, outputs, and latest run.
170
+ openmapstack inspect path/to/project.yaml
171
+ ```
172
+
173
+ Useful automation options:
174
+
175
+ ```bash
176
+ openmapstack validate project.yaml --json --output validation/cli-report.json
177
+ openmapstack validate project.yaml --strict # warnings also return non-zero
178
+ openmapstack validate project.yaml --preflight # skip not-yet-generated artifacts
179
+ openmapstack run project.yaml --dry-run
180
+ openmapstack run project.yaml --json
181
+ openmapstack inspect project.yaml --json
182
+ ```
183
+
184
+ ### `openmapstack verify` — check the analysis, not just the paperwork
185
+
186
+ `validate` audits the manifest and its bookkeeping. `verify` runs the check
187
+ library in `openmapstack/checks/` against what the pipeline actually produced:
188
+ geometry read back through DuckDB Spatial, dataset CRS read from the artifact
189
+ rather than the manifest's claim, validation evidence recomputed from the
190
+ geodata it summarises, and QGIS project structure and runtime loading where
191
+ PyQGIS is available.
192
+
193
+ ```bash
194
+ openmapstack verify path/to/project.yaml
195
+ openmapstack verify path/to/project.yaml --rerun # + rebuild from source and compare
196
+ openmapstack verify path/to/project.yaml --json --output validation/verify-report.json
197
+ openmapstack verify path/to/project.yaml --strict # warnings and not-testable also return 1
198
+ ```
199
+
200
+ These checks require no repository-owned golden answer, so they work on data
201
+ neither this repository nor the model has seen. They establish bounded
202
+ structural, provenance, artifact, and reproducibility predicates; they do not
203
+ prove every project-specific analytical answer.
204
+
205
+ `--rerun` is the strongest signal available without a known answer. It rebuilds
206
+ the project in an empty workspace from only the manifest, the declared
207
+ immutable inputs, and the declared dependencies, runs the one canonical
208
+ entrypoint, re-hashes the sources, and compares the outputs semantically. A
209
+ pipeline that cannot reproduce itself, or that mutates its own declared
210
+ immutable inputs, is not trustworthy whatever its numbers say.
211
+
212
+ The check plan is derived from the manifest rather than configured, so a
213
+ project cannot opt out of a check by omitting it: a declared output is a
214
+ checked output. A check whose dependency is missing reports `not_testable` and
215
+ is counted separately — never a silent pass. A mixture of executed and
216
+ `not_testable` checks has aggregate status `warning`, and every report includes
217
+ `applicable`, `executed`, and `execution_rate` coverage. Install
218
+ `openmapstack[geo]` for the DuckDB-backed geodata checks; PyQGIS comes from a
219
+ system QGIS install.
220
+
221
+ See [the applicability reference](docs/verify-applicability.md) for the exact
222
+ plan conditions, dependencies, current regression evidence, and deliberate
223
+ exclusions. In particular, browser/dashboard checks are not yet part of the
224
+ automatic `verify` plan.
225
+
226
+ Project-specific known answers can be declared under
227
+ `validation.expectations[]`. The five allowlisted checks cover row count,
228
+ feature presence/absence, one feature-field value, and field range. New
229
+ expectations start as `attestation.status: unverified`; they produce a warning
230
+ and are not executed. The JSON report supplies the exact
231
+ `expected_expectation_sha256` an independent reviewer must bind, together with
232
+ the current `runs.latest.inputs_hash`. Changing the expected check, arguments,
233
+ inputs, or a retained local evidence file invalidates the attestation and
234
+ returns it to warning status. See
235
+ [the project contract](references/project-spec.md#26-validation).
236
+
237
+ `validate` checks manifest structure, source retrieval/version/licensing data,
238
+ CRS declarations, processing graph resolution, override provenance and files,
239
+ output existence, validation-report parity/status propagation, override
240
+ application results, and run-record identity/hashes. GIS-specific checks such as
241
+ geometry validity remain the pipeline's responsibility; the CLI verifies that
242
+ each declared check appears exactly once with an explicit result.
243
+
244
+ Normal validation warnings return exit code 0 so known limitations remain
245
+ representable. Failures return 1; malformed invocation or an unstartable runtime
246
+ returns 2. `--strict` makes warnings return 1.
247
+
248
+ ## What this skill will and won't do
249
+
250
+ **Will:**
251
+ - Recommend modern, cloud-native formats (GeoParquet, COG, PMTiles) and flag legacy patterns (Shapefile output, MBTiles for new deployments).
252
+ - Push spatial joins to DuckDB / PostGIS instead of Python loops.
253
+ - Discover data via STAC before downloading.
254
+ - Preserve license metadata (OSM ODbL, Overture per-source, Sentinel attribution).
255
+ - Pin dataset versions for reproducibility (Overture releases, STAC item IDs, OSM extract dates).
256
+ - Compile material multi-stage analysis into a reproducible GIS project (`project.yaml` + pipeline + overrides + validation), deriving the final map/dashboard from it.
257
+
258
+ **Won't:**
259
+ - Trigger on simple location lookups ("what city is this?") or casual map references with no analytical work.
260
+ - Default to proprietary services when an open/self-hosted option fits the scale, quality, privacy, and budget.
261
+
262
+ ## License
263
+
264
+ Licensed under the [MIT License](LICENSE).
265
+
266
+ ## Contributing
267
+
268
+ Issues and PRs welcome at [github.com/jaakla/openmapstack](https://github.com/jaakla/openmapstack). When adding a new tool or workflow, place it in the matching reference file and add a one-row entry to the relevant decision matrix in [SKILL.md](SKILL.md).
@@ -0,0 +1,251 @@
1
+ # openmapstack
2
+
3
+ **Geospatial questions → reproducible, validated GIS analysis project (with very nice interactive map).**
4
+
5
+ Install:
6
+ ```bash
7
+ npx skills add jaakla/openmapstack -g
8
+ ```
9
+
10
+ OpenMapStack gives your favorite AI agent: Claude Code, Codex, Cursor, OpenCode, and 50+ other agents a production workflow from **authoritative data discovery** through analysis to interactive web and GIS deliverables. Material workflows become inspectable and repeatable well-defined projects in a `yaml` file with pinned sources, explicit assumptions and CRS choices, deterministic processing, isolated overrides, machine-readable validation, and surfaced provenance.
11
+
12
+ It is open-first and cloud-native by default, built on shoulders of the awesome Open GIS stack: STAC for discovery; GeoParquet, COG, and PMTiles for storage and delivery; DuckDB and PostGIS for compute; and QGIS, MapLibre, and Martin for presentation. It also uses GDAL/OGR, GeoPandas, xarray/rioxarray, PDAL, routing engines, spatial SQL, and pragmatic hosted services when scale or reliability requires them.
13
+
14
+ ## What's in this repo
15
+
16
+ - [SKILL.md](SKILL.md) — the skill entry point: triggers, global defaults, format and compute decision matrices, anti-patterns, and a quick triage guide.
17
+ - [references/data-sources.md](references/data-sources.md) - lists OSM, Overture, Sentinel/Landsat, regional portals, STAC catalogs and others.
18
+ - [references/services-and-scale.md](references/services-and-scale.md) - depending on case use local installs or hosted/SaaS services for global-scale basemaps, elevation, routing, geocoding, place search, and postcodes.
19
+ - [references/formats-and-crs.md](references/formats-and-crs.md) - how to choose formats, conversions, projections, EPSG codes.
20
+ - [references/processing.md](references/processing.md) - when and how to use GDAL/OGR, GeoPandas, xarray, DuckDB, PostGIS, PDAL and other open geo processing tools.
21
+ - [references/analytics.md](references/analytics.md) — do vector/raster analytics, terrain, hydrology, network, point clouds, geocoding etc.
22
+ - [references/web-delivery.md](references/web-delivery.md) — renderer selection for maps, PMTiles, MVT, Martin, TiTiler, MapLibre, deck.gl, kepler.gl, and lonboard formats and engines.
23
+ - [references/qgis.md](references/qgis.md) — QGIS desktop, plugins, PyQGIS, Processing, QGIS MCP.
24
+ - [references/validation-and-ops.md](references/validation-and-ops.md) — validation, manifests, attribution, and deployment checks, including the machine-readable reproducible-project contract.
25
+ - [references/project-spec.md](references/project-spec.md) — the specific`openmapstack-project/v1` schema: compiling any material analysis into a reproducible GIS project (`project.yaml`, pipeline, source provenance, overrides, validation, semantic presentation, QGIS output).
26
+ - [templates/](templates/) — ready scaffolds (`project.yaml`, `pipeline.py`, `presentation.yaml`, `validation.yaml`) for new projects.
27
+ - [examples/tartu-development/](examples/tartu-development/) — a fully-worked reproducible project matching the acceptance scenario: source provenance + timestamps, explicit assumptions, two verified project overrides (a scenario attribute change with prior-value verification, and hypothetical scenario geometry), deterministic pipeline, machine-readable validation, and semantic presentation.
28
+ - [evals/](evals/) — the eval suite grading whether an agent reaches the right analytical answer, respects the GIS-method guardrails, and reruns reproducibly, with the `openmapstack-project/v1` contract as the substrate that makes those independently checkable: `python evals/run.py --mode fixture` runs deterministic, no-LLM checks against real generated artifacts (analytical correctness against known geospatial truth, metric CRS, source immutability, schema, overrides, validation integrity, presentation contract, and clean reruns), plus adversarial cases and a pluggable live-agent benchmark (Claude Code, Codex, and any OpenAI-compatible API such as OpenRouter — URL and model via `OPENAI_COMPATIBLE_*` env, API key as a secret).
29
+ - [`openmapstack/`](openmapstack/) — the installable `openmapstack validate/run/inspect` CLI for auditing and executing `openmapstack-project/v1` projects, plus [`openmapstack/checks/`](openmapstack/checks/): the reusable, semantic check library. All but five of its checks are oracle-free, so the same functions that grade the eval suite also grade a user's own project on data this repository has never seen.
30
+ - [`.claude-plugin/`](.claude-plugin/) — Claude Code plugin and marketplace manifests, so the repository can also be installed with `/plugin install`. Validated in CI by [`.github/workflows/plugin.yml`](.github/workflows/plugin.yml).
31
+
32
+ My local Estonia-specific guidance (Maa- ja Ruumiamet, ETAK, EPSG:3301 / L-EST97) is included for convenience. But all the global sources are incuded for world-wide coverage.
33
+
34
+ ## Install
35
+
36
+ The recommended way is the [skills CLI](https://github.com/vercel-labs/skills), which works for Claude Code, Cursor, OpenCode, Codex, and 50+ other agents.
37
+
38
+ ### Recommended: skills CLI
39
+
40
+ Install globally (available in every project):
41
+
42
+ ```bash
43
+ npx skills add jaakla/openmapstack -g
44
+ ```
45
+
46
+ Update later with `npx skills update open-map-stack`. Remove with `npx skills remove open-map-stack`.
47
+
48
+ ### Claude Code plugin (optional)
49
+
50
+ Claude Code users can install the same repository as a plugin instead. This adds
51
+ versioned installs, `/plugin update`, and project-scoped installs that a team
52
+ picks up from a repository's `.claude/settings.json`:
53
+
54
+ ```bash
55
+ /plugin marketplace add jaakla/openmapstack
56
+ /plugin install open-map-stack@open-map-stack
57
+ ```
58
+
59
+ The repository is its own marketplace, so no separate marketplace repo is
60
+ needed. The plugin wraps the same root `SKILL.md` — nothing is duplicated, and
61
+ the skills-CLI install path above keeps working unchanged.
62
+
63
+ ### Install the project CLI
64
+
65
+ The skills installer loads the agent instructions; the Python package provides
66
+ the project commands. From a clone of this repository:
67
+
68
+ ```bash
69
+ python3 -m pip install .
70
+ openmapstack --version
71
+ ```
72
+
73
+ For development, the commands can also run directly without installation:
74
+
75
+ ```bash
76
+ python3 -m openmapstack --help
77
+ ```
78
+
79
+ ### Manual install (fallback)
80
+
81
+ If you'd rather not use the CLI, clone directly into your agent's skills directory. For Claude Code:
82
+
83
+ ```bash
84
+ # User-level (every project)
85
+ git clone https://github.com/jaakla/openmapstack.git ~/.claude/skills/open-map-stack
86
+
87
+ # Project-level (one repo)
88
+ git clone https://github.com/jaakla/openmapstack.git .claude/skills/open-map-stack
89
+ ```
90
+
91
+ ### Verify
92
+
93
+ Start Claude Code and run `/skills open-map-stack` should appear in the list. The expected layout is:
94
+
95
+ ```
96
+ <skills-dir>/open-map-stack/
97
+ ├── SKILL.md
98
+ ├── references/
99
+ │ ├── analytics.md
100
+ │ ├── data-sources.md
101
+ │ ├── formats-and-crs.md
102
+ │ ├── processing.md
103
+ │ ├── project-spec.md
104
+ │ ├── qgis.md
105
+ │ ├── services-and-scale.md
106
+ │ ├── spatial-sql.md
107
+ │ ├── validation-and-ops.md
108
+ │ └── web-delivery.md
109
+ ├── templates/
110
+ │ ├── project.yaml
111
+ │ ├── pipeline.py
112
+ │ ├── presentation.yaml
113
+ │ └── validation.yaml
114
+ ├── examples/
115
+ │ └── tartu-development/
116
+ └── .claude-plugin/ # Claude Code plugin + marketplace manifests
117
+ ├── plugin.json
118
+ └── marketplace.json
119
+ ```
120
+
121
+ ## Use
122
+
123
+ The skill auto-activates when you ask Claude about geospatial work — terms like GIS, OpenStreetMap, Overture, Sentinel, Landsat, LiDAR, GeoTIFF, shapefile, GeoPackage, raster/vector tiles, isochrones, spatial joins, EPSG codes, and projections will all trigger it. You don't need to invoke it manually, but sometimes hinting "use open-map-stack skills" helps.
124
+
125
+ Example prompts that engage the skill:
126
+
127
+ - "Pull all buildings in Tartu from Overture and publish them as a PMTiles layer."
128
+ - "Compute average NDVI for these polygons from Sentinel-2 over the last 12 months."
129
+ - "Reproject this GeoTIFF from EPSG:3301 to EPSG:3857 as a COG."
130
+ - "Set up an OSRM routing server from a Estonia OSM extract."
131
+ - "Build an isochrone API around these points."
132
+
133
+ If you want to force the skill to load, you can reference it explicitly:
134
+
135
+ > Use the open-map-stack skill to convert this shapefile to GeoParquet.
136
+
137
+ ## Project CLI
138
+
139
+ The CLI operates on an `openmapstack-project/v1` manifest. A project directory may
140
+ be supplied in place of its `project.yaml` file.
141
+
142
+ ```bash
143
+ # Audit the complete artifact, including outputs, report, and run record.
144
+ openmapstack validate path/to/project.yaml
145
+
146
+ # Check the produced artifacts without requiring a golden answer.
147
+ openmapstack verify path/to/project.yaml
148
+
149
+ # Run the one canonical pipeline, then validate what it produced.
150
+ openmapstack run path/to/project.yaml
151
+
152
+ # Review sources, versions, overrides, ordered steps, outputs, and latest run.
153
+ openmapstack inspect path/to/project.yaml
154
+ ```
155
+
156
+ Useful automation options:
157
+
158
+ ```bash
159
+ openmapstack validate project.yaml --json --output validation/cli-report.json
160
+ openmapstack validate project.yaml --strict # warnings also return non-zero
161
+ openmapstack validate project.yaml --preflight # skip not-yet-generated artifacts
162
+ openmapstack run project.yaml --dry-run
163
+ openmapstack run project.yaml --json
164
+ openmapstack inspect project.yaml --json
165
+ ```
166
+
167
+ ### `openmapstack verify` — check the analysis, not just the paperwork
168
+
169
+ `validate` audits the manifest and its bookkeeping. `verify` runs the check
170
+ library in `openmapstack/checks/` against what the pipeline actually produced:
171
+ geometry read back through DuckDB Spatial, dataset CRS read from the artifact
172
+ rather than the manifest's claim, validation evidence recomputed from the
173
+ geodata it summarises, and QGIS project structure and runtime loading where
174
+ PyQGIS is available.
175
+
176
+ ```bash
177
+ openmapstack verify path/to/project.yaml
178
+ openmapstack verify path/to/project.yaml --rerun # + rebuild from source and compare
179
+ openmapstack verify path/to/project.yaml --json --output validation/verify-report.json
180
+ openmapstack verify path/to/project.yaml --strict # warnings and not-testable also return 1
181
+ ```
182
+
183
+ These checks require no repository-owned golden answer, so they work on data
184
+ neither this repository nor the model has seen. They establish bounded
185
+ structural, provenance, artifact, and reproducibility predicates; they do not
186
+ prove every project-specific analytical answer.
187
+
188
+ `--rerun` is the strongest signal available without a known answer. It rebuilds
189
+ the project in an empty workspace from only the manifest, the declared
190
+ immutable inputs, and the declared dependencies, runs the one canonical
191
+ entrypoint, re-hashes the sources, and compares the outputs semantically. A
192
+ pipeline that cannot reproduce itself, or that mutates its own declared
193
+ immutable inputs, is not trustworthy whatever its numbers say.
194
+
195
+ The check plan is derived from the manifest rather than configured, so a
196
+ project cannot opt out of a check by omitting it: a declared output is a
197
+ checked output. A check whose dependency is missing reports `not_testable` and
198
+ is counted separately — never a silent pass. A mixture of executed and
199
+ `not_testable` checks has aggregate status `warning`, and every report includes
200
+ `applicable`, `executed`, and `execution_rate` coverage. Install
201
+ `openmapstack[geo]` for the DuckDB-backed geodata checks; PyQGIS comes from a
202
+ system QGIS install.
203
+
204
+ See [the applicability reference](docs/verify-applicability.md) for the exact
205
+ plan conditions, dependencies, current regression evidence, and deliberate
206
+ exclusions. In particular, browser/dashboard checks are not yet part of the
207
+ automatic `verify` plan.
208
+
209
+ Project-specific known answers can be declared under
210
+ `validation.expectations[]`. The five allowlisted checks cover row count,
211
+ feature presence/absence, one feature-field value, and field range. New
212
+ expectations start as `attestation.status: unverified`; they produce a warning
213
+ and are not executed. The JSON report supplies the exact
214
+ `expected_expectation_sha256` an independent reviewer must bind, together with
215
+ the current `runs.latest.inputs_hash`. Changing the expected check, arguments,
216
+ inputs, or a retained local evidence file invalidates the attestation and
217
+ returns it to warning status. See
218
+ [the project contract](references/project-spec.md#26-validation).
219
+
220
+ `validate` checks manifest structure, source retrieval/version/licensing data,
221
+ CRS declarations, processing graph resolution, override provenance and files,
222
+ output existence, validation-report parity/status propagation, override
223
+ application results, and run-record identity/hashes. GIS-specific checks such as
224
+ geometry validity remain the pipeline's responsibility; the CLI verifies that
225
+ each declared check appears exactly once with an explicit result.
226
+
227
+ Normal validation warnings return exit code 0 so known limitations remain
228
+ representable. Failures return 1; malformed invocation or an unstartable runtime
229
+ returns 2. `--strict` makes warnings return 1.
230
+
231
+ ## What this skill will and won't do
232
+
233
+ **Will:**
234
+ - Recommend modern, cloud-native formats (GeoParquet, COG, PMTiles) and flag legacy patterns (Shapefile output, MBTiles for new deployments).
235
+ - Push spatial joins to DuckDB / PostGIS instead of Python loops.
236
+ - Discover data via STAC before downloading.
237
+ - Preserve license metadata (OSM ODbL, Overture per-source, Sentinel attribution).
238
+ - Pin dataset versions for reproducibility (Overture releases, STAC item IDs, OSM extract dates).
239
+ - Compile material multi-stage analysis into a reproducible GIS project (`project.yaml` + pipeline + overrides + validation), deriving the final map/dashboard from it.
240
+
241
+ **Won't:**
242
+ - Trigger on simple location lookups ("what city is this?") or casual map references with no analytical work.
243
+ - Default to proprietary services when an open/self-hosted option fits the scale, quality, privacy, and budget.
244
+
245
+ ## License
246
+
247
+ Licensed under the [MIT License](LICENSE).
248
+
249
+ ## Contributing
250
+
251
+ Issues and PRs welcome at [github.com/jaakla/openmapstack](https://github.com/jaakla/openmapstack). When adding a new tool or workflow, place it in the matching reference file and add a one-row entry to the relevant decision matrix in [SKILL.md](SKILL.md).
@@ -0,0 +1,6 @@
1
+ """Command-line tooling for reproducible OpenMapStack projects."""
2
+
3
+ from .validation import Check, ValidationResult, validate_project
4
+
5
+ __all__ = ["Check", "ValidationResult", "validate_project"]
6
+ __version__ = "0.2.0"
@@ -0,0 +1,3 @@
1
+ from .cli import main
2
+
3
+ raise SystemExit(main())
@@ -0,0 +1,112 @@
1
+ """Reusable, semantic checks over an `openmapstack-project/v1` artifact.
2
+
3
+ Every check has the signature:
4
+
5
+ def fn(workspace: Path, **args) -> AssertionResult
6
+
7
+ and inspects real files in `workspace` (a project directory) rather than
8
+ assistant prose. Checks never raise for expected "could not check"
9
+ conditions — they return `not_testable` instead, matching the four-state
10
+ vocabulary (`passed | failed | warning | not_testable`) required of
11
+ `validation/latest-report.json` itself in `references/project-spec.md`
12
+ section 6.
13
+
14
+ Two callers share this library, deliberately:
15
+
16
+ - `evals/run.py` grades eval cases with it (`contract_ci`, `mutation_tests`,
17
+ `agent_benchmark`, `integration_visual`);
18
+ - `openmapstack verify` grades a *user's own* project with it.
19
+
20
+ That is the point of it living in the shipped package rather than under
21
+ `evals/`. All but a handful of these checks are oracle-free: they hold for
22
+ any correct project on any data, so they transfer to data this repository
23
+ has never seen. The exceptions — the ones that need a known answer — are
24
+ `geodata.row_count(equals=)`, `feature_present`, `feature_absent`,
25
+ `feature_field_equals`, and `field_range`. On user data those are reachable
26
+ only through allowlisted, input-bound `validation.expectations` attestations,
27
+ never from an answer the pipeline computed and certified for itself.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import json
33
+ from dataclasses import dataclass, field
34
+ from pathlib import Path
35
+ from typing import Any
36
+
37
+ import yaml
38
+
39
+ STATUSES = ("passed", "failed", "warning", "not_testable")
40
+
41
+
42
+ @dataclass
43
+ class AssertionResult:
44
+ status: str # passed | failed | warning | not_testable
45
+ detail: str = ""
46
+ data: dict[str, Any] = field(default_factory=dict)
47
+
48
+ def __post_init__(self) -> None:
49
+ if self.status not in STATUSES:
50
+ raise ValueError(f"invalid assertion status: {self.status!r}")
51
+
52
+ def to_dict(self) -> dict[str, Any]:
53
+ return {"status": self.status, "detail": self.detail, **self.data}
54
+
55
+
56
+ def passed(detail: str = "", **data: Any) -> AssertionResult:
57
+ return AssertionResult("passed", detail, data)
58
+
59
+
60
+ def failed(detail: str = "", *, code: str | None = None, **data: Any) -> AssertionResult:
61
+ """``code`` is a stable, machine-readable failure identifier (e.g.
62
+ ``feature_present``). Mutation cases can require the *specific* failure
63
+ they inject, not merely status ``failed``, via ``expect_code`` in
64
+ ``expected.yaml``."""
65
+ if code is not None:
66
+ data["code"] = code
67
+ return AssertionResult("failed", detail, data)
68
+
69
+
70
+ def warning(detail: str = "", *, code: str | None = None, **data: Any) -> AssertionResult:
71
+ if code is not None:
72
+ data["code"] = code
73
+ return AssertionResult("warning", detail, data)
74
+
75
+
76
+ def not_testable(detail: str = "", *, code: str | None = None, **data: Any) -> AssertionResult:
77
+ if code is not None:
78
+ data["code"] = code
79
+ return AssertionResult("not_testable", detail, data)
80
+
81
+
82
+ def load_project_yaml(workspace: Path, project_dir: str = ".") -> dict[str, Any] | None:
83
+ path = workspace / project_dir / "project.yaml"
84
+ if not path.exists():
85
+ return None
86
+ try:
87
+ with path.open("r", encoding="utf-8") as fh:
88
+ value = yaml.safe_load(fh)
89
+ except (OSError, yaml.YAMLError):
90
+ return None
91
+ return value if isinstance(value, dict) else None
92
+
93
+
94
+ def load_json(path: Path) -> dict[str, Any] | None:
95
+ if not path.exists():
96
+ return None
97
+ with path.open("r", encoding="utf-8") as fh:
98
+ return json.load(fh)
99
+
100
+
101
+ def project_root(workspace: Path, project_dir: str = ".") -> Path:
102
+ return workspace / project_dir
103
+
104
+
105
+ def get_in(data: dict, dotted: str, default: Any = None) -> Any:
106
+ """Fetch a nested dict value using a dotted path, e.g. 'project.status'."""
107
+ cur: Any = data
108
+ for part in dotted.split("."):
109
+ if not isinstance(cur, dict) or part not in cur:
110
+ return default
111
+ cur = cur[part]
112
+ return cur