From eb79aab4637bed45bd07d3097df7d311050fbf3d Mon Sep 17 00:00:00 2001 From: Claudio Ortega Date: Sun, 9 Aug 2026 20:46:50 -0700 Subject: [PATCH 1/2] docs: define UrbanPy 0.3 production roadmap --- docs/development/current-state-audit.md | 186 ++++++++++ docs/development/osrm-0.3.md | 334 ++++++++++++++++++ docs/development/typed-models-0.3.md | 244 +++++++++++++ docs/development/urbanpy-0.3-project.md | 432 ++++++++++++++++++++++++ 4 files changed, 1196 insertions(+) create mode 100644 docs/development/current-state-audit.md create mode 100644 docs/development/osrm-0.3.md create mode 100644 docs/development/typed-models-0.3.md create mode 100644 docs/development/urbanpy-0.3-project.md diff --git a/docs/development/current-state-audit.md b/docs/development/current-state-audit.md new file mode 100644 index 0000000..e449276 --- /dev/null +++ b/docs/development/current-state-audit.md @@ -0,0 +1,186 @@ +# UrbanPy current-state audit for the 0.3 roadmap + +Audit date: 2026-08-09 + +This is a planning baseline, not a claim that every defect has been reproduced on +every platform. Repository facts were inspected locally at commit `ddd0360`; live +GitHub/PyPI/tool facts should be refreshed when implementation begins. + +## Executive assessment + +UrbanPy has a useful domain scope and recent performance improvements, but the +current default branch is not release-ready. The highest risks are the unprotected +merge path, nondeterministic live-service tests, release credential/workflow +history, unresolved dependency-license status, legacy packaging, and an OSRM path +that can report success without a usable server. + +The 0.3 roadmap is viable if work is sequenced as stabilization and release-chain +hardening first, then packaging/typing/OSRM migration, then public API and release +polish. Treating uv, Trunk, Pydantic, or an AI reviewer as a standalone solution +would leave the main production risks untouched. + +## Repository baseline + +- Default branch: `master` at `ddd0360` (merged PR #59). +- Package code plus tests: approximately 3,425 Python lines in the current tree. +- Packaging: legacy `setup.py`; package version is rewritten during release; + `python_requires` still says Python 3.6 or newer. +- Dependency input: `requirements.txt` pins more than 100 direct and transitive + packages together. `environment.yml` and docs requirements are additional, + inconsistent sources. +- Latest OSMnx metadata requires Python 3.11 or newer, while current CI tests 3.9, + 3.10, and 3.11. The declared and tested support policies do not agree. +- PyPI's latest stable release is 0.2.1 from 2021; development releases appeared + in 2023. Repository tags, PyPI releases, and source versioning need reconciliation. +- `urbanpy.__init__` eagerly imports all feature modules. Optional feature imports + therefore cannot be optional, and HDX configuration is created at import time. +- `urbanpy/routing/osrm_routing.py` duplicates functionality in `routing.py` but is + not part of the documented export path. +- Bash and PowerShell files are tracked but are not included as package data by the + current wheel configuration. +- Pydantic is pinned in the dependency file but not used by UrbanPy; it appears as + dependency-tree baggage rather than an intentional contract. + +## Pull-request and automation baseline + +Current repository workflows visibly provide: + +- test/docs/release/deploy workflow; +- SonarQube scan; +- CodeQL; +- GitGuardian; +- organization-level License Compliance status; +- Copilot pull-request review. + +Findings: + +- The organization ruleset requires a PR and blocks deletion/non-fast-forward + updates, but currently requires zero approvals, no thread resolution, and no + successful status checks. +- The latest inspected PR (#60) is unstable: Python build jobs fail, while docs, + SonarQube, CodeQL, and GitGuardian pass. +- Six geometry tests fail because live Nominatim requests return HTTP 403; the + response body is then parsed as JSON. Other tests call live HDX and OSMnx/Overpass. +- The OSRM test is deselected in CI rather than replaced by unit/contract coverage. +- The same PR received duplicate test/docs runs from overlapping push and PR events. +- The License Compliance status reports 104 issues and has no result URL. The exact + findings require owner/tool investigation; the failure must not be waived blindly. +- SonarQube dependency installation and coverage are marked best-effort, failures + are suppressed, and `sonar.sources=.` makes source/test/notebook scope ambiguous. +- The scan job can pass without proving that tests or coverage succeeded and does + not explicitly wait for the quality gate in repository configuration. +- CI still uses old major Actions and several floating `@master`/`@latest` action + references. Current runner logs warn about forced Node runtime compatibility. +- The combined workflow gives release/deploy logic a very large blast radius and + mutates source files to create a version. + +## Test and API baseline + +- The current suite collects roughly 15 tests. Most geometry tests and the HDX + test depend on changing public data/services. +- Tests include a maintainer's personal email address as Nominatim identification. +- There are placeholder/pass tests for important Google and ORS behavior. +- Assertions sometimes compare `.all()` booleans instead of numeric arrays and + several expected live results are inherently unstable. +- HTTP calls generally omit explicit timeouts and consistent status handling. +- Nominatim currently uses `requests`' default User-Agent, contrary to public + service identification requirements, which explains the observed 403 risk. +- Broad `except Exception`, printing, and mixed sentinels (`None`, `-1`, `NaN`, + raw `Response`) make failure behavior hard to automate or type safely. +- Coordinate order, CRS, and seconds/minutes are not consistently encoded in names + or types. Some docstrings and examples disagree with current functions. +- Public exports are controlled by star imports and module `__all__` lists, with no + compatibility snapshot or explicit stability classification. + +## OSRM baseline + +- The API requires hand-authored `country` and `continent` fragments. +- Geofabrik nested paths and slash-containing canonical IDs are unsupported. +- The official Geofabrik v1 catalog already supplies unique IDs, parents, ISO + aliases, and complete PBF URLs with a versioned stable structure. +- Bash and PowerShell implementations diverge and hard-code platform paths. +- Docker image, port, temporary names, sleeps, cleanup, and error output are not + safe for concurrent or automated use. +- The MLD command sequence uses the PBF filename for partition/customize/routed, + rather than the prepared `.osrm` base shown by OSRM's supported flow. +- No manifest proves which PBF/profile/image created existing artifacts. + +## Security, licensing, and community baseline + +- Recent repository history includes removal of a malicious workflow and an open + PR to replace an exposed PyPI token. Incident closure and credential revocation + must be evidenced, not inferred from file deletion alone. +- Secret scanning and push protection are enabled, which is a useful existing + control to preserve. +- GPL-3 is stated, but package metadata and an EL-BID-approved precise SPDX/license + compatibility policy are not present. +- There is no `SECURITY.md`, CODEOWNERS policy, support policy, or release ownership + document in the inspected tree. +- README and Sphinx docs still recommend Python 3.6-era environments and old + operating systems; README also contains obsolete analytics beacon markup. +- Citation exists as BibTeX text but not `CITATION.cff`. +- Contribution guidance references an internal repository/automatic merge process + that does not match the visible public workflow. + +## Existing issue/PR reconciliation + +| Existing item | 0.3 disposition | +| --- | --- | +| PR #60 trusted publishing | Phase 0; fix CI, verify credential revocation, then merge/supersede | +| PR #58 Sonar config | Triage as likely superseded by merged Sonar files; preserve intent | +| PRs #54–#56 dependency bumps | Re-evaluate after direct-dependency/uv migration; do not merge stale full-lock pins blindly | +| PR #46 satellite embeddings | Separate feature review; not on critical path by default | +| #21 nested Geofabrik regions | Absorb into OSRM catalog resolver | +| #26 Windows OSRM | Absorb into one cross-platform Python manager | +| #29 missing OSRM scripts | Close by eliminating script-owned logic and wheel-test the replacement | +| #38 latest H3 incompatibility | Phase 2 compatibility gate | +| #20 OSMnx nearest-node API | Phase 2 compatibility gate | +| #27 CRS on generated frames | Phase 2 correctness gate | +| #19 and #52 HDX failures | Shared typed HTTP/client boundary plus regression fixtures | +| #14 merge reuse bug | Reproduce and fix in Phase 2 | +| #15 download API compatibility | Address through versioning, release notes, and deprecation policy | +| #16 Conda distribution | Submit to conda-forge after stable PyPI 0.3, not a private release job | +| #28 notebook docs | Convert maintained examples; archive stale outputs | +| #30 masked HDX retrieval | Feasibility decision; defer if upstream format prevents real savings | +| #31 progress feedback | Presentation adapter after operations expose progress events | + +## Plan changes resulting from this review + +The original plans were directionally sound but incomplete. This review adds: + +- a Phase 0 stabilization/security gate before new architecture; +- supply-chain, license, SBOM, credential, and provenance work as a separate + workstream; +- exact branch-rules and human-review requirements; +- a vendor-neutral, least-privilege operating model for remote AI agents; +- a measured CodeRabbit/Copilot review pilot rather than an automatic required bot; +- explicit Trunk adoption criteria and standard-tool fallback; +- external-service compliance and scheduled live contracts; +- direct dependency/optional import architecture and Python 3.11+ recommendation; +- correct OSRM `.osm.pbf` to `.osrm` sequencing, manifests, atomic downloads, + locking, readiness, labeling, and safe cleanup; +- public versus internal Pydantic model taxonomy, schema stability, redaction, + static typing, and migration rules; +- measurable phase gates, issue Definition of Ready, PR Definition of Done, and + post-release support. + +## Primary external references + +- uv project locking/syncing: +- uv package build/publish: +- Geofabrik catalog schema: +- OSRM backend: +- Pydantic documentation: +- SonarQube quality gates: + +- GitHub rulesets: + +- GitHub sub-issues and dependencies: + +- Nominatim usage policy: + +- CodeRabbit plans: +- Greptile pricing: +- conda-forge package contribution: + + diff --git a/docs/development/osrm-0.3.md b/docs/development/osrm-0.3.md new file mode 100644 index 0000000..56bd9e9 --- /dev/null +++ b/docs/development/osrm-0.3.md @@ -0,0 +1,334 @@ +# OSRM and Geofabrik plan for UrbanPy 0.3 + +## Outcome + +Provide a cross-platform, observable OSRM lifecycle that downloads the correct +Geofabrik extract, prepares it reproducibly, and starts or stops a local service +without embedded Bash or PowerShell implementations. + +This epic closes or supersedes issues #21, #26, and #29. It also removes the +duplicate lifecycle implementation in `urbanpy/routing/osrm_routing.py`. + +## Current defects to eliminate + +- URLs are assembled from `continent` and `country`, which cannot represent the + Geofabrik hierarchy reliably. +- scripts are not included in installed wheels even though Python calls them; +- Bash and PowerShell implement different paths and behavior; +- the current MLD sequence passes `.osm.pbf` to partition/customize/routed where + OSRM's prepared `.osrm` dataset base is required; +- the image is floating, processing container names are global, and port 5000 is + hard-coded; +- slash-containing region IDs are unsafe as filesystem/container names; +- failures are often printed and then reported as success; +- downloads are not atomic or resumable and concurrent preparation is unsafe; +- there is no disk-space preflight, readiness deadline, structured status, + ownership label, or targeted cleanup policy. + +## Architecture decisions + +- Python owns orchestration. Shell files, if retained temporarily, only invoke the + public Python CLI and contain no lifecycle logic. +- Docker commands are argument arrays executed with `shell=False`. +- Use the official OSRM backend image pinned to an explicit supported version and + record the resolved digest in the preparation manifest. Validate supported CPU + architectures before selecting the tag. +- Use Geofabrik's `index-v1-nogeom.json` as the catalog and `urls.pbf` verbatim. + UrbanPy never constructs a PBF URL from region path fragments. +- The exact Geofabrik `properties.id` is canonical, including `/`. ISO 3166-1 and + ISO 3166-2 codes are lookup aliases. Display names are search text only. +- Silent fuzzy matching is prohibited. Ambiguity raises an error containing + candidate IDs and parents. +- Store catalog cache, downloaded PBFs, prepared datasets, state, and logs in + platform-appropriate locations with explicit user overrides. +- Library methods return typed results and raise typed exceptions. Rendering + progress and messages is a CLI/notebook adapter concern. +- Prepared data is immutable for a specific region, profile, PBF identity, OSRM + image, and algorithm. Reuse requires an exact manifest match. + +## Proposed package layout + +The final layout can vary, but responsibilities must remain separated. + +```text +urbanpy/ + geofabrik/ + catalog.py # fetch/cache/index/resolve + models.py # internal transport plus public region model + routing/ + models.py # OSRM configuration/status/results + osrm.py # high-level manager + osrm_client.py # route/table HTTP client, independent of Docker + _docker.py # injectable command runner + _download.py # streamed atomic downloads + _paths.py # platform paths and safe resource identifiers + cli/ + osrm.py # presentation and exit codes +``` + +No network, Docker, or filesystem work occurs at import time. + +## Public contracts + +### Geofabrik catalog + +`GeofabrikCatalog` provides: + +- `get(region_id)` for an exact canonical ID; +- `resolve(value, parent=None)` with priority exact ID, exact ISO code, then exact + case-folded display name only when unambiguous; +- `search(text, parent=None)` returning candidates without choosing one; +- `list(parent=None, downloadable_only=True)`; +- `refresh(force=False)` and explicit offline behavior. + +`GeofabrikRegion` includes canonical ID, display name, parent ID, ISO aliases, +public PBF URL, and optional ancestry derived from the catalog. It does not expose +the internal-authenticated `pbf-internal` URL. + +Catalog validation checks the v1 FeatureCollection envelope, unique IDs, valid +parent references, expected list-valued ISO fields, and HTTPS public URLs. Unknown +third-party fields are tolerated for forward compatibility. The cache honors ETag +and Last-Modified, has an explicit freshness policy, and can fall back to the last +valid copy with a surfaced stale status. Corrupt or partially written catalogs are +never promoted. + +### OSRM configuration + +`OSRMConfig` contains at least: + +- `region_id`; +- `profile`: car, bicycle, or foot; +- `algorithm`: MLD for 0.3; +- host bind address (loopback by default) and host port; +- data/cache path overrides; +- official image reference; +- download, command, and readiness timeouts; +- refresh/rebuild policy; +- optional resource limits supported consistently by Docker. + +Profile configuration distinguishes the Lua preparation profile from the OSRM API +profile token. The mapping is explicit and tested. + +### OSRM manager + +`OSRMManager` provides: + +- `plan()` — resolve inputs and return paths, sizes where known, commands, and + reuse decisions without mutation; +- `prepare()` — download and build a complete prepared dataset; +- `start()` — start or reuse a service and wait for readiness; +- `status()` — return container, manifest, endpoint, and readiness information; +- `stop()` — stop only the matching UrbanPy-owned service; +- `logs()` — stream or read logs without hiding subprocess failures; +- `clean()` — remove explicitly selected UrbanPy-owned containers/artifacts. + +The manager is also a context manager. Context exit stops a container it started +but does not delete downloaded or prepared data unless explicitly requested. + +### OSRM HTTP client + +`OSRMClient` is independent of Docker and accepts any configured OSRM base URL. It +provides typed route/table/nearest operations, explicit timeouts, status/error +translation, response-size limits, and unit-preserving results. Existing routing +functions delegate to this client, which allows the same UrbanPy API to use a +managed local service or an externally operated OSRM endpoint. + +## Lifecycle + +### 1. Resolve and plan + +- Resolve the exact catalog region and use its `urls.pbf`. +- Derive a Docker/filesystem-safe identifier from a readable slug plus a stable + short hash of the canonical ID. Never use a raw `/` in a resource name. +- Resolve image tag/digest, profile mapping, storage paths, and port. +- Inspect existing manifests and containers through exact labels, not substring + matches in `docker ps` output. + +### 2. Preflight + +- Check Docker CLI presence, daemon access, image architecture, writable storage, + port availability, and a conservative free-space requirement. +- Explain Docker-not-installed, daemon-not-running, permission, port, disk, and + unsupported-platform errors separately. +- Ask the CLI user to confirm unexpectedly large downloads/processing estimates + unless a non-interactive confirmation flag is supplied. + +### 3. Download + +- Stream to a `.part` file with progress, connect/read timeouts, and bounded + retries. +- Use ETag/Last-Modified and `If-Range` for safe resume when supported. Restart a + partial download if the remote identity changed. +- Enforce HTTPS, expected Geofabrik public host policy, response-size limits, and + available disk space. +- Atomically rename only after the response is complete and validation succeeds. +- Record source URL, validators, byte size, retrieval time, and local digest in a + manifest. Do not claim upstream checksum verification unless an authoritative + checksum was actually retrieved and verified. + +### 4. Prepare + +- Acquire a per-dataset inter-process lock. +- Run `osrm-extract` with the selected Lua profile against `.osm.pbf`. +- Run `osrm-partition` and `osrm-customize` against the resulting `.osrm` base. +- Use unique, labeled, automatically removed processing containers so an + interrupted run cannot collide with another region/profile. +- Capture stdout/stderr to structured logs while preserving live progress. +- Build in a staging directory and atomically publish the complete prepared set + plus manifest. A failed run never looks reusable. + +### 5. Start and verify + +- Run a detached, labeled service container with a read-only data mount and the + selected host bind/port. Bind to `127.0.0.1` by default; external exposure is an + explicit opt-in with a security warning. +- Use an exact, collision-safe name incorporating region/profile identity. +- Poll until the container is running and the OSRM HTTP endpoint returns a + well-formed OSRM response, with a deadline and captured diagnostic logs. +- A timeout stops only the container created by that invocation and raises an + actionable readiness error. No unbounded busy loops or fixed success sleeps. + +### 6. Stop and clean + +- Find resources by exact UrbanPy ownership/manifest labels. +- `stop()` is idempotent and never removes data. +- `clean()` requires explicit scopes such as container, prepared profile, PBF, or + catalog cache; dry-run is available and default for broad selections. +- Never remove an unknown container or directory merely because its name contains + `osrm`. + +## Compatibility strategy + +For 0.3, current functions remain available: + +```python +start_osrm_server(country, continent, profile) +stop_osrm_server(country, continent, profile) +``` + +They delegate to `OSRMManager`, resolve `country` as an exact catalog ID/alias, +validate legacy continent/path hints against ancestry where possible, and emit a +visible `FutureWarning`. Legacy underscore path workarounds may be recognized only +in this adapter and are never used to construct URLs. + +The preferred API is region-centric: + +```python +config = OSRMConfig(region_id="us/new-york", profile="foot", port=5001) +with OSRMManager(config) as server: + result = OSRMClient(server.endpoint).route(origin, destination) +``` + +If return values or units of `osrm_route`/`compute_osrm_dist_matrix` change, add a +new typed API first and keep legacy tuple/array behavior through the documented +deprecation window. + +## CLI + +An optional `urbanpy osrm` CLI uses the same manager: + +```text +urbanpy osrm regions search new-york +urbanpy osrm plan --region us/new-york --profile foot +urbanpy osrm prepare --region us/new-york --profile foot +urbanpy osrm start --region us/new-york --profile foot --port 5001 +urbanpy osrm status --region us/new-york --profile foot +urbanpy osrm logs --region us/new-york --profile foot +urbanpy osrm stop --region us/new-york --profile foot +urbanpy osrm clean --region us/new-york --profile foot --prepared --dry-run +``` + +Commands support non-interactive operation, machine-readable JSON output, stable +exit codes, Ctrl-C cancellation, and redaction of sensitive environment values. + +## GitHub sub-issues + +1. Write ADR and public lifecycle/error/compatibility contract. +2. Add Pydantic region/config/status models and safe identifiers. +3. Implement and fixture-test the Geofabrik v1 catalog parser/index. +4. Implement exact ID, ISO, ancestry, search, and ambiguity-aware resolution. +5. Implement conditional catalog caching and explicit offline/stale behavior. +6. Implement streamed atomic/resumable PBF downloads and manifests. +7. Implement platform paths, dataset locks, staging, and atomic publication. +8. Implement injectable Docker discovery, inspection, execution, and error mapping. +9. Implement preflight planning for daemon, architecture, port, storage, and reuse. +10. Implement correct pinned MLD extract/partition/customize preparation. +11. Implement labeled start, loopback binding, readiness, status, logs, and stop. +12. Implement a Docker-independent typed OSRM route/table/nearest HTTP client. +13. Implement dry-run and targeted cleanup safeguards. +14. Add CLI adapters, JSON output, progress, cancellation, and exit codes. +15. Delegate legacy functions and add tested deprecation warnings. +16. Remove duplicate module and orchestration scripts after compatibility lands. +17. Add unit, contract, subprocess, concurrency, and failure-injection tests. +18. Add opt-in Docker integration and cross-platform manual acceptance workflow. +19. Verify installed sdist/wheel behavior and publish operations/troubleshooting docs. + +Each sub-issue must define its exact public surface and test doubles. Do not combine +catalog resolution, Docker orchestration, and legacy removal in one PR. + +## Verification matrix + +### Hermetic PR tests + +- fixture regions: `peru`, nested `sul`, slash-containing `us/new-york`, a region + without ISO, multiple ISO aliases, missing PBF URL, invalid parent, duplicate ID, + malformed catalog, ambiguity, and stale cache; +- exact subprocess argument and label assertions for Linux, macOS, and Windows path + forms; +- `.osm.pbf` versus `.osrm` command sequencing; +- partial download resume/change, atomic failure, insufficient disk, occupied port, + missing Docker, daemon denial, image failure, command failure, readiness timeout, + Ctrl-C, concurrent prepare, and targeted cleanup; +- manifest-match reuse and mismatch-triggered rebuild; +- legacy signature/return/deprecation tests. + +PR tests require neither Docker nor network access. + +### Scheduled/manual integration + +- resolve and download a deliberately small Geofabrik extract; +- prepare it with the pinned image; +- start OSRM, call route/table endpoints, validate units, and stop it; +- rerun to prove idempotent reuse; +- inject a second profile/port to prove isolation; +- clean only resources created by the job. + +Automate Linux Docker integration. Record release-candidate manual evidence from +maintainers using Docker Desktop on macOS (including Apple Silicon where supported) +and Windows. A platform is not claimed as supported without evidence. + +## Acceptance criteria + +- `urls.pbf` from the official catalog is the only download URL source. +- Correct OSRM `.osm.pbf` to `.osrm` preparation is asserted in tests. +- macOS, Linux, and Windows use identical Python lifecycle logic. +- image, dataset, profile, and algorithm identity is reproducible in a manifest. +- concurrent and interrupted runs cannot publish corrupt prepared state. +- start succeeds only after bounded readiness verification. +- local services bind to loopback by default and the HTTP client works with both + managed-local and explicitly configured remote endpoints. +- stop/clean operations can affect only exact UrbanPy-owned resources. +- legacy users receive a working adapter and documented migration path. +- installed wheel behavior, not merely repository execution, passes smoke tests. +- SonarQube, deterministic CI, security, and dependency compliance pass. + +## Out of scope for 0.3 + +- hosting or operating a public OSRM service; +- silently fuzzy-matching region names; +- automatic planet-scale preparation; +- arbitrary custom Lua profiles; +- incremental Geofabrik replication updates; +- non-Docker container engines; +- packaging PBF or prepared graph data inside UrbanPy artifacts. + +## Authoritative references + +- Geofabrik technical catalog: + +- Geofabrik no-geometry catalog: + +- OSRM backend and Docker quick start: + +- OSRM HTTP API: + diff --git a/docs/development/typed-models-0.3.md b/docs/development/typed-models-0.3.md new file mode 100644 index 0000000..8870ac6 --- /dev/null +++ b/docs/development/typed-models-0.3.md @@ -0,0 +1,244 @@ +# Pydantic and safe data-type plan for UrbanPy 0.3 + +## Outcome + +Use Pydantic v2 and static annotations to make configuration, external service +payloads, and intentionally stable public results explicit and safe while +preserving native geospatial objects for computation. + +Pydantic is currently present only as one of many pinned packages in +`requirements.txt`; UrbanPy does not use it. If UrbanPy imports Pydantic directly, +it becomes a declared direct dependency with a tested version range. + +## Boundary-first policy + +Use Pydantic at boundaries where untrusted or serialized data enters or leaves: + +- public configuration and CLI input; +- HTTP request parameters and the external JSON fields UrbanPy consumes; +- durable cache records and OSRM preparation manifests; +- stable public result/configuration objects; +- parsing legacy dictionaries, tuples, and environment values. + +Keep these native in computational paths: + +- `GeoDataFrame` and `GeoSeries`; +- pandas and NumPy arrays/scalars; +- Shapely geometries; +- NetworkX/OSMnx graphs; +- rasters and other large domain arrays. + +Do not create one model per GeoDataFrame row or validate large arrays element by +element by default. Validate the container contract once—geometry type, CRS, +required columns, shape, coordinate conventions—and let the native library own +the bulk data. + +Static typing and runtime validation are complementary. All supported public +Python APIs receive precise annotations; only boundary data receives runtime +validation. + +## Model taxonomy and stability + +### Public domain models + +Place intentionally supported contracts under `urbanpy.models` or documented +feature namespaces. Examples: + +- `Coordinate`: named `longitude` and `latitude`, finite and range-checked; +- `BoundingBox`: `west`, `south`, `east`, `north`, with ordering and antimeridian + behavior explicit; +- `TravelProfile`: constrained values and service-specific mappings; +- `GeofabrikRegion`: canonical region identity, parent, ISO aliases, and public + PBF URL; +- `OSRMConfig`: region, profile, image, port, paths, timeouts, and policy; +- `OSRMStatus`: lifecycle state, endpoint, dataset/image identity, and diagnostics; +- `RouteResult`: explicit `distance_m` and `duration_s`, with optional geometry + only if a stable serialization is specified; +- matrix metadata describing row/column identity and units without wrapping the + underlying NumPy array. + +These models follow the normal compatibility/deprecation policy. Model field names, +defaults, JSON serialization, and generated schema are public API. + +### Internal transport models + +Third-party payload schemas live in internal client modules, for example +`urbanpy._clients.geofabrik`. They model only consumed fields, tolerate documented +additional upstream fields, and translate into UrbanPy domain models. They are not +re-exported and can evolve with upstream APIs without creating public API debt. + +### Lightweight typed structures + +Use `TypedDict`, `Protocol`, `TypeAlias`, `NamedTuple`, or a standard dataclass when +runtime validation/serialization is not needed. `TypeAdapter` can validate a +collection or `TypedDict` without inventing a public `BaseModel`. + +Do not make exceptions into Pydantic models. Define a small, stable UrbanPy +exception hierarchy that wraps transport and validation failures while preserving +safe structured context. + +## Validation conventions + +- Use Pydantic v2 APIs and `ConfigDict`; do not add v1 compatibility code. +- Use strict validation when coercion could conceal coordinate, unit, profile, + identifier, boolean, or port mistakes. Permit intentional convenience coercion + only in a named legacy/input adapter with tests. +- UrbanPy-owned configuration uses `extra="forbid"`. Third-party transport models + normally ignore unknown fields but require all fields UrbanPy actually uses. +- Prefer immutable/frozen value models where mutation would invalidate a manifest + or cache identity. +- Do not enable `arbitrary_types_allowed=True` globally. A native object accepted + without validation should stay outside the model or have a focused adapter. +- Reject non-finite coordinates and numeric values. State whether Z coordinates + are ignored, rejected, or preserved. +- Encode units in field names (`distance_m`, `duration_s`) and docs, not only prose. +- Coordinate tuple adapters must state and test order. Public models use named + fields to avoid `(lat, lon)` versus `(lon, lat)` ambiguity. +- CRS-bearing inputs use shared validation helpers and `pyproj.CRS` normalization; + a missing CRS is never silently assumed for an operation where it affects + correctness. +- Secrets use redacted representations and never appear in validation errors, + model dumps, logs, or `repr`. +- Validation errors report a stable error category, field path, expected form, + and remediation without echoing entire external payloads. +- JSON serialization is deterministic where used for manifests/cache keys. + +## API migration rules + +- Normalize a legacy input once at the boundary; internal code receives the new + model or native normalized type. +- New APIs may accept a model plus documented plain-Python conveniences. +- Avoid decorating every public function with `validate_call`; it changes error + semantics and adds overhead. Use explicit boundary adapters where behavior is + controllable and benchmarked. +- When replacing a tuple/dict result would break unpacking or equality, introduce + a parallel typed API first. Keep the legacy result adapter for the documented + deprecation window. +- Public deprecations use a visible warning category, correct `stacklevel`, docs, + tests, changelog entry, and a scheduled removal version. +- Pydantic `ValidationError` may be preserved as the cause, but the public API + raises a documented UrbanPy boundary/configuration exception where consistency + matters. +- Do not silently change coordinate order, CRS, distance unit, duration unit, or + null/sentinel behavior during model adoption. + +## Static typing plan + +- Select one stable CI type checker in an ADR. Mypy is the initial recommendation + because Pydantic maintains a v2 plugin; verify behavior with current geospatial + stubs before committing. +- Add `py.typed` only when annotations shipped to users are checked and supported. +- Start strictness with new model/client/OSRM modules, then migrate one existing + module at a time. Do not hide the entire current package behind blanket ignores. +- Add and pin needed stub packages in the type-check dependency group, not runtime. +- Use protocols at subprocess/HTTP boundaries to make fakes type-safe and avoid + coupling tests to concrete clients. +- Type-check code examples that claim to be supported. + +## Initial shared model slice + +The Pydantic epic should not block all OSRM work. Its first independently +deliverable slice is: + +1. `Coordinate` and `BoundingBox` plus native geometry/container validators; +2. `TravelProfile` and explicit OSRM API profile mapping; +3. `GeofabrikRegion` plus internal v1 catalog transport models; +4. `OSRMConfig`, manifest identity, and `OSRMStatus`; +5. base exceptions and safe validation-error translation. + +Freeze this slice only after the catalog and Docker manager consumers have been +implemented together in tests. Avoid designing unused generic models. + +## Module migration order + +1. **Geofabrik/OSRM:** best-defined new boundary and immediate production need. +2. **Shared HTTP configuration/errors:** timeouts, endpoints, identification, + retry/caching policy, and safe response metadata. +3. **Routing APIs:** coordinates, profiles, distance/duration results, matrix + dimensions/units, and provider responses. +4. **Download clients:** Nominatim, Overpass, and HDX request/response boundaries. +5. **Geometry/accessibility:** validate CRS, geometry kind, columns, resolution, + and numeric parameters without wrapping whole frames. +6. **Plotting:** validate small option/config inputs only if current errors justify + runtime models. + +Every module migration includes behavior characterization first. Existing defects +are not frozen as compatibility unless explicitly documented. + +## GitHub sub-issues + +1. Inventory public inputs/outputs, current sentinels, coordinate order, CRS, and + units across all exported functions. +2. Write ADR for runtime versus static typing and model stability boundaries. +3. Select Pydantic/Python/type-checker ranges and measure dependency impact. +4. Define exception, units, CRS, naming, serialization, and deprecation conventions. +5. Implement/test coordinate, bounds, profile, and safe URL primitives. +6. Implement internal Geofabrik v1 transport and public region model. +7. Implement OSRM configuration, manifest identity, status, and result models. +8. Add schema snapshots and generated schema documentation for public models. +9. Add public package typing baseline and type-safe HTTP/subprocess protocols. +10. Migrate routing boundaries while preserving legacy tuple/array adapters. +11. Migrate Nominatim/Overpass/HDX boundaries and typed service errors. +12. Add GeoDataFrame/GeoSeries precondition helpers for CRS, columns, and geometry. +13. Migrate geometry/accessibility boundaries module by module. +14. Benchmark validation/import/serialization cost on representative workloads. +15. Publish model/API migration guidance and deprecation schedule. + +Each implementation issue must name a real boundary and consumer. “Add models for +module X” is not sufficiently defined. + +## Verification + +- Unit tests cover valid, invalid, boundary, strict/coercion, redaction, + serialization, and round-trip behavior. +- Property-based tests cover finite coordinate ranges, bounding-box ordering, + region identifiers, ports, timeouts, and matrix dimensions. +- Fixture contract tests cover extra/missing/null/wrong-type third-party fields + without live network calls. +- Schema snapshots detect accidental field/default/serialization changes. +- Compatibility tests cover old signatures, tuple/array ordering, warnings, and + exception translation. +- Static type tests cover production modules and supported examples, including + negative/error examples where the checker supports them. +- Benchmarks compare import time, single-object validation, catalog parsing, and + representative large GeoDataFrame operations. Large frames receive no per-row + Pydantic pass by default. +- Built-wheel tests confirm `py.typed`, public imports, and generated schemas are + actually packaged. +- SonarQube continues to analyze migrated code and remains required. + +## Acceptance criteria + +- Every public model has a documented boundary, owner, consumer, stability level, + example, and schema test. +- Every internal transport model translates into a domain/native type and is not + accidentally re-exported. +- The OSRM/Geofabrik path has no unchecked dictionary traversal. +- Coordinates, CRS, distance units, duration units, and null behavior are explicit. +- Large geospatial objects incur no per-row Pydantic validation by default. +- Secrets and full external payloads cannot leak through validation errors/logs. +- Existing callers have a tested adapter and migration path for changed behavior. +- Static annotations, runtime behavior, documentation, and packaged schemas agree. +- Validation overhead stays within an agreed budget and is negligible relative to + network/geospatial work at the chosen boundaries. + +## Out of scope for 0.3 + +- replacing GeoPandas, pandas, Shapely, NumPy, or NetworkX domain objects; +- validating every dataframe row or array element; +- exposing raw third-party response models as permanent UrbanPy API; +- using Pydantic models merely to increase model count or typing coverage; +- environment-driven global settings without a demonstrated use case; +- changing all existing return values in one breaking migration. + +## Authoritative references + +- Pydantic models and configuration: +- Pydantic strict mode: + +- Pydantic performance guidance: + +- Pydantic mypy plugin: + +- PEP 561 typed packages: + diff --git a/docs/development/urbanpy-0.3-project.md b/docs/development/urbanpy-0.3-project.md new file mode 100644 index 0000000..30c044f --- /dev/null +++ b/docs/development/urbanpy-0.3-project.md @@ -0,0 +1,432 @@ +# UrbanPy 0.3 production-readiness project + +## Mission + +Ship UrbanPy 0.3 as a reproducible, typed, tested, documented, and maintainable +geospatial library that EL-BID can safely recommend to external users and that +new contributors can improve without relying on maintainer-specific machines. + +This is the parent roadmap for the GitHub Project **UrbanPy 0.3 — Production +Readiness**. Large workstreams are parent issues. Work that can be reviewed and +verified independently is a sub-issue and normally one pull request. + +The evidence behind this roadmap is recorded in +[`current-state-audit.md`](current-state-audit.md). The OSRM and runtime-model +workstreams have dedicated plans: + +- [`osrm-0.3.md`](osrm-0.3.md) +- [`typed-models-0.3.md`](typed-models-0.3.md) + +## Release principles + +- SonarQube remains enabled and required for EL-BID compliance. +- Pull requests with human approval are the only path to the default branch. +- PR checks are deterministic and do not call live public geospatial services. +- The package declares direct dependencies; the development lock records a + reproducible environment. Published wheels do not pin every transitive package. +- Public API compatibility is intentional, documented, and tested. +- Runtime validation is concentrated at untrusted and serialized boundaries. +- Native GeoPandas, pandas, NumPy, Shapely, and NetworkX types remain native in + computational paths. +- AI agents may prepare branches and PRs, but may not approve, merge, change + repository security, or publish releases. +- 0.3 scope favors reliability over adding new data sources or algorithms. + +## Decisions to record before implementation + +Each decision becomes a short architecture decision record (ADR), with owner, +date, alternatives, consequences, and reversal plan. + +1. **Python support.** The recommended baseline is Python 3.11 through 3.14. + Current OSMnx releases require Python 3.11 or newer. Confirm wheel availability + for every direct dependency and operating system before finalizing it. +2. **Build and versioning.** Use `pyproject.toml`, `uv`, a PEP 517 build backend, + and one authoritative version source. Remove tag-time source rewriting. +3. **Dependency boundaries.** Decide which capabilities are base installation + requirements and which become extras, then remove eager imports that defeat + those boundaries. +4. **Runtime typing.** Adopt the boundary-first Pydantic policy in the dedicated + plan and select the static type checker used in CI. +5. **OSRM lifecycle.** Adopt the Python-owned, catalog-backed architecture in the + dedicated OSRM plan. +6. **External services.** Define common timeouts, identification, retries, + caching, errors, and live-contract testing for Nominatim, Overpass, HDX, ORS, + Google Maps, Geofabrik, and OSRM. +7. **License policy.** EL-BID confirms the precise project SPDX expression and + approves a dependency-license allowlist and exception process. Tool output is + evidence, not legal advice. +8. **Default branch.** Treat a possible `master` to `main` rename as a separate, + coordinated migration. It is not required to make 0.3 technically sound. + +## Workstreams and parent issues + +### W0. Project governance and issue triage + +**Outcome:** contributors can see the scope, decision owners, dependencies, and +release gates without reconstructing them from old issues. + +- Create the organization-level GitHub Project and the fields/views below. +- Create parent issues W0 through W10 and attach the dedicated plans. +- Link existing issues and PRs instead of creating duplicates. +- Add issue forms, a pull-request template, `CODEOWNERS`, `SECURITY.md`, + `SUPPORT.md`, and a lightweight governance/maintainer policy. +- Record ADRs for the eight decisions above. +- Define supported-version, deprecation, and vulnerability-response policies. +- Assign at least two humans to release-critical ownership where possible. + +### W1. Packaging, `uv`, and dependency architecture + +**Outcome:** users install a small, correct package; contributors reproduce CI +with documented `uv` commands. + +- Replace `setup.py`, the fully pinned runtime `requirements.txt`, and duplicated + metadata with PEP 621 metadata in `pyproject.toml`. +- Declare only direct runtime dependencies with justified lower/upper bounds. +- Define PEP 735 groups for test, lint/type, docs, security, and release tooling. +- Define extras only after import boundaries make them real, for example + `plotting`, `hdx`, `google`, and `cli`. +- Commit the cross-platform `uv.lock`; CI uses `uv sync --locked` and checks + `uv lock --check`. +- Add a `.python-version` for the contributor default without limiting the CI + compatibility matrix. +- Add `urbanpy.__version__`, complete package metadata/project URLs, classifiers, + and `py.typed` if the public typing contract is accepted. +- Stop importing every optional module and initializing HDX configuration during + `import urbanpy`. +- Build sdist and wheel with `uv build`; run metadata, contents, `pip check`, and + clean-install smoke tests on both artifacts. +- Verify that installed-wheel behavior—not only source-tree behavior—is tested. +- Retire `environment.yml` as a dependency authority. Keep a generated example + only if it adds user value. + +### W2. Deterministic CI, Trunk, and required quality gates + +**Outcome:** one stable set of checks answers whether a PR is mergeable. + +- Split the monolithic workflow into PR CI, SonarQube, documentation, scheduled + contracts, and release workflows with narrow triggers. +- Add concurrency cancellation, job timeouts, dependency caching, minimal token + permissions, and `persist-credentials: false` where checkout credentials are + unnecessary. +- Pin third-party Actions to immutable commit SHAs and automate reviewed updates. +- Prevent duplicate `push` and `pull_request` runs for the same contribution. +- Make one stable aggregate check (`CI / required`) represent the supported + Python matrix so branch rules do not depend on changing matrix job names. +- Run formatting/linting, static typing, hermetic unit tests with coverage, docs + with warnings as errors, and artifact checks. +- Perform a time-boxed Trunk Code Quality spike. If it remains suitable, use + Trunk locally and in CI to orchestrate pinned Ruff, format, Markdown/YAML, + ShellCheck/shfmt, actionlint, and other repository checks. Keep the standard + tool configurations authoritative so contributors and remote agents have a + direct `uv run` fallback. +- Do not enable Trunk Flaky Tests quarantine until tests are hermetic and the + team agrees that quarantine cannot conceal regressions. +- Preserve CodeQL, secret scanning/push protection, and applicable EL-BID checks. + +#### SonarQube hardening + +- Keep the SonarQube scan mandatory. +- Remove all `continue-on-error` and `|| true` behavior from required analysis. +- Produce coverage once in the test workflow and pass the exact artifact to the + scan; a missing or invalid report fails the job. +- Set `sonar.sources=urbanpy`, `sonar.tests=tests`, coverage paths, Python versions, + and focused exclusions instead of analyzing the entire repository as source. +- Enable quality-gate waiting or reliable PR decoration so a failed gate fails + the GitHub check. +- Confirm the EL-BID SonarQube edition/configuration supports the intended PR + analysis and record the quality gate applied to new code. + +#### Default-branch rules + +- Require one human approval, resolution of review threads, and approval after + the latest material push (or dismissal of stale approvals). +- Require `CI / required`, the SonarQube quality gate, security analysis, and the + approved license/vulnerability check from their expected GitHub Apps. +- Block deletion and force pushes and require PRs to be up to date or use a + tested merge queue. +- Keep AI review advisory; it never satisfies the human-approval requirement. + +### W3. Supply-chain security, licensing, and release credentials + +**Outcome:** the dependency and release chain has an auditable disposition. + +- Complete incident closure for the exposed PyPI credential and removed + malicious workflow: revoke/rotate credentials, review audit logs and release + history, document impact, and close the relevant security work. +- Merge or supersede PR #60 only after its workflow is green and trusted + publishing is configured end to end. +- Remove long-lived PyPI tokens; use a protected GitHub environment and OIDC + trusted publishing with `id-token: write` only in the publish job. +- Triage the current “License Compliance” failure instead of suppressing it. +- Generate a dependency inventory and CycloneDX SBOM from the `uv` resolution. +- Scan locked dependencies for known vulnerabilities and define severity/SLA and + exception-expiry policy. +- Review direct and transitive dependency licenses against the EL-BID-approved + policy; store machine-readable output and human decisions separately. +- Add OpenSSF-friendly metadata, `SECURITY.md`, dependency update automation, and + release provenance/attestations where supported. +- Remove obsolete analytics beacons and accidental personal identifiers from + tests and documentation. + +### W4. Safe, always-on agentic development + +**Outcome:** remote agents can continuously deliver reviewable work without +receiving release or administrative authority. + +- Add concise repository instructions describing bootstrap commands, architecture, + allowed test doubles, style, verification, and protected areas. +- Create an `agent:ready` issue label. Eligible issues must have bounded scope, + dependencies resolved, acceptance criteria, expected files/interfaces, and + exact verification commands. +- Agents work on short-lived branches and open draft PRs. They may not push to the + default branch, merge, approve, modify rulesets/secrets, or publish packages. +- Use least-privilege short-lived credentials; workflows from forks receive no + secrets and untrusted code never runs with `pull_request_target` privileges. +- Require PR bodies to identify generated changes, tests run, unresolved risks, + API/dependency effects, and the issue closed. +- Require CODEOWNER review for workflows, packaging/release, security, public + models, and compatibility shims. +- Measure agent PR rework, escaped defects, review latency, and abandoned work; + adjust task size and instructions based on evidence. + +#### AI code-review pilot + +- Continue to treat GitHub Copilot review as advisory while measuring its signal. +- Pilot one additional reviewer at a time to avoid duplicate noise. CodeRabbit is + the leading free candidate because public open-source repositories currently + receive its OSS review tier. Greptile's OSS program is limited to qualifying + MIT/Apache projects, so GPL-licensed UrbanPy would rely on its individual + Starter allowance rather than the OSS program. +- Run the pilot for at least 20 representative PRs. Track actionable findings, + false-positive rate, duplicate comments, time-to-review, data access, permissions, + and maintainer effort. +- Do not make an AI reviewer a required check. Human review, tests, SonarQube, + and deterministic static analysis remain authoritative. + +### W5. Test architecture and external-service reliability + +**Outcome:** PR failures indicate code regressions, while scheduled jobs detect +upstream service drift. + +- Divide tests into `unit`, `contract`, `integration`, `docker`, and `live` + markers with explicit ownership and budgets. +- Replace PR-time Nominatim, HDX, OSMnx/Overpass, ORS, Google, Geofabrik, and OSRM + calls with recorded minimal fixtures and HTTP/subprocess fakes. +- Block accidental network access in the unit-test job. +- Add small live contract tests on a schedule and manual dispatch with clear + provider-friendly rates; their failure opens or updates an issue but does not + retroactively make unrelated PRs fail. +- Build a shared HTTP boundary with an identifying User-Agent, explicit connect + and read timeouts, bounded retries with jitter, `raise_for_status`, typed error + translation, optional endpoint override, and response-size limits. +- Respect provider policy, rate limits, caching headers, attribution, and privacy. + Nominatim usage in particular must stay at or below its public-service limit, + identify UrbanPy, and be switchable/configurable. +- Add deterministic fixtures for CRS, coordinate order, geometry validity, units, + empty data, nulls, and disconnected networks. +- Use coverage as a ratchet: no regression in total coverage and at least 80% + coverage on new code, aligned with the SonarQube quality gate. +- Add a small benchmark suite for the performance work merged in PR #59 and fail + only on agreed, repeatable regressions. + +### W6. OSRM and Geofabrik lifecycle + +**Outcome:** local OSRM is reproducible and cross-platform, and extract selection +does not depend on fragile path naming. + +Implement the dedicated [`osrm-0.3.md`](osrm-0.3.md). It absorbs issues #21, +#26, and #29 and retires the duplicate `osrm_routing.py` implementation and the +platform-specific orchestration scripts. + +### W7. Pydantic models, static typing, and API contracts + +**Outcome:** untrusted inputs and stable public results have safe, documented +contracts without slowing geospatial computation. + +Implement [`typed-models-0.3.md`](typed-models-0.3.md). Start with shared OSRM and +Geofabrik boundaries, then migrate external service clients module by module. + +### W8. Geospatial correctness and existing defect backlog + +**Outcome:** supported features behave correctly with the current geospatial +ecosystem and state their CRS/coordinate/unit assumptions. + +- Fix and regression-test current H3 compatibility (#38). +- Fix current OSMnx API and coordinate-order compatibility (#20). +- Guarantee or explicitly require CRS on created and consumed objects (#27). +- Resolve HDX failures (#19 and #52) through the shared service boundary. +- Reproduce and fix `merge_shape_hex` re-use behavior (#14). +- Decide whether masked HDX population retrieval (#30) is feasible without + misleading performance claims; otherwise defer it from 0.3. +- Add meaningful progress reporting for genuinely long operations (#31) without + coupling library functions to one terminal renderer. +- Audit every `__all__` export and classify it as supported, deprecated, internal, + or removed. Add public API and signature snapshots. +- Replace broad exception swallowing, sentinel mixes (`None`, `-1`, `NaN`), and + library-level `print` calls with documented results, warnings, and exceptions. +- Standardize longitude/latitude order, CRS expectations, and distance/duration + units across routing, download, geometry, and accessibility modules. + +New feature PR #46 is evaluated separately. It is not on the 0.3 critical path +unless it satisfies the new data-source, dependency, test, and maintenance rules. + +### W9. Documentation and community readiness + +**Outcome:** installation, common workflows, limits, and contribution paths are +accurate for 0.3. + +- Keep Sphinx for 0.3 unless a short ADR demonstrates that migration pays for + itself; update it and build with warnings as errors. +- Rewrite installation and quickstart material against built artifacts and `uv`. +- Add task-oriented guides for downloads, H3 geometry, accessibility, remote + routing APIs, and local OSRM. +- Add an external-service matrix covering credentials, endpoint configuration, + rate limits, caching, attribution, privacy, failure modes, and live-test policy. +- Publish API reference, model JSON Schemas where useful, and a 0.3 migration guide. +- Turn maintained notebooks into deterministic examples or documentation tests; + archive stale outputs and notebooks that cannot be reproduced (#28). +- Add `CITATION.cff`, accurate author/maintainer information, governance, support, + security reporting, and a maintenance-status statement. +- Document OSM/Geofabrik attribution and data licensing separately from UrbanPy's + software license. +- After a stable PyPI release, pursue a community-maintained conda-forge recipe + rather than a private Anaconda channel (#16). + +### W10. Release engineering and post-release operation + +**Outcome:** exactly the reviewed artifacts are promoted, published, and supported. + +- Use separate build and publish workflows; build once and promote the same + immutable sdist/wheel artifacts. +- Trigger releases from protected, signed `v*` tags or an approved release + workflow; validate tag and package versions without modifying source files. +- Publish alpha, beta, and release-candidate artifacts to TestPyPI and exercise + documented clean-install smoke tests before production. +- Publish PyPI through trusted publishing, create GitHub release notes/changelog, + and deploy documentation from the released version. +- Define rollback/yank criteria, post-release smoke tests, and on-call ownership. +- Monitor installation failures, security reports, upstream service contracts, + and top user issues for at least four weeks after 0.3.0. + +## Delivery phases and gates + +### Phase 0 — Stabilize and secure + +Required before new architecture work is merged: + +- credential/security incident closed; +- PR test workflow is green and hermetic; +- SonarQube remains operational and is no longer best-effort; +- default-branch rules require human review and successful checks; +- current license-compliance failure has an owner and documented disposition; +- old issues and PRs are triaged into the Project. + +### Phase 1 — 0.3.0a1 foundation + +- `pyproject.toml`, supported Python range, `uv.lock`, and direct dependencies; +- stable lint/type/test/docs/package commands locally and in CI; +- artifact install tests and trusted-publishing dry run; +- Pydantic primitives and Geofabrik catalog models; +- OSRM catalog resolver and command-runner foundations. + +### Phase 2 — 0.3.0b1 functional migration + +- Python-owned OSRM prepare/start/status/stop lifecycle; +- typed external-service boundaries and consistent error behavior; +- priority compatibility issues (#14, #19, #20, #27, #38, #52) resolved; +- user and contributor documentation updated. + +### Phase 3 — 0.3.0rc1 release hardening + +- full supported Python/OS matrix green; +- manual OSRM acceptance evidence on macOS, Linux, and Windows; +- migration guide and API compatibility report complete; +- SBOM, vulnerability, dependency-license, package-content, and provenance gates + pass; +- no unresolved P0/P1 defects or unowned high risks. + +### Phase 4 — 0.3.0 and follow-through + +- publish reviewed artifacts through trusted publishing; +- deploy versioned documentation and release notes; +- run post-release installation and OSRM smoke tests; +- begin conda-forge submission and four-week support observation. + +## GitHub Project configuration + +Recommended fields: + +- Status: Triage, Backlog, Ready, In progress, In review, Blocked, Done +- Workstream: W0 through W10 +- Priority: P0, P1, P2 +- Work type: Decision, Implementation, Test, Documentation, Security, Release +- Target: Phase 0, 0.3.0a1, 0.3.0b1, 0.3.0rc1, 0.3.0, Post-0.3 +- Risk: Low, Medium, High +- Effort: XS, S, M, L; split anything larger than L +- Agent readiness: Not suitable, Needs decision, Ready, Agent working +- Owner + +Enable GitHub's native **Parent issue**, **Sub-issue progress**, and issue +dependency relationships rather than duplicating dependency state in a text +field. + +Recommended views: + +- Roadmap grouped by target and workstream +- Current iteration board +- Release gates (`priority:P0,P1` or `work-type:Release,Security`) +- Agent-ready queue +- Blocked work with dependency relationships +- Existing bugs and community contributions + +Milestones represent published checkpoints. The Project represents execution. +Labels describe stable properties such as `area:osrm`, `area:typing`, +`area:packaging`, `breaking-change`, `good-first-issue`, `needs-design`, and +`agent:ready`. + +## Issue definition of ready + +Before an issue moves to Ready it has: + +- one testable outcome and an explicit non-goal; +- linked parent, dependencies, and existing issue/PR history; +- public API, dependency, security, data-license, and migration impact identified; +- acceptance criteria and exact local verification commands; +- fixture/test strategy that avoids unapproved live services; +- owner/reviewer and estimated effort no larger than L. + +## Pull-request definition of done + +- linked issue acceptance criteria are satisfied; +- tests cover success, failure, and compatibility behavior; +- `uv lock --check` passes and dependency changes are explained; +- required lint, type, unit, docs, package, security, license, and SonarQube checks + pass; +- public behavior, schemas, changelog, and migration notes are updated; +- generated/AI-assisted changes are disclosed and independently reviewed; +- no unrelated cleanup is bundled into the PR; +- one human reviewer approves after the latest material change. + +## Definition of done for UrbanPy 0.3 + +- A new contributor can clone, bootstrap, check, test, build, and preview docs + using documented `uv` and Trunk/fallback commands. +- PRs cannot merge without human approval, deterministic green CI, SonarQube, + security analysis, and approved dependency compliance. +- Supported Python versions and operating systems pass clean wheel-install tests. +- `import urbanpy` has no network side effects and optional capabilities have + honest dependency boundaries. +- OSRM uses one Python implementation on all platforms, canonical Geofabrik IDs + and URLs, pinned container inputs, safe storage, readiness checks, and targeted + cleanup. +- New boundary models provide actionable errors and stable schemas while large + geospatial objects avoid per-row runtime validation. +- External-service calls identify UrbanPy, set timeouts, respect provider policy, + and have hermetic PR tests plus scheduled live contracts. +- Public APIs state coordinate order, CRS, units, null behavior, and exceptions; + compatibility changes have tests and migration notes. +- Built sdist/wheel contents, metadata, license, SBOM, provenance, and clean + installation are verified before trusted publication. +- Documentation, citation, governance, security, support, and release ownership + are current, and no P0/P1 issue or unowned high risk remains. + From 95ce8c86ac03a977fd7f9024a3e75dff6400e14b Mon Sep 17 00:00:00 2001 From: Claudio Ortega Date: Sun, 9 Aug 2026 20:47:44 -0700 Subject: [PATCH 2/2] docs: record dependency security baseline --- docs/development/current-state-audit.md | 5 ++++- docs/development/urbanpy-0.3-project.md | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/development/current-state-audit.md b/docs/development/current-state-audit.md index e449276..70d9d48 100644 --- a/docs/development/current-state-audit.md +++ b/docs/development/current-state-audit.md @@ -65,6 +65,10 @@ Findings: - The same PR received duplicate test/docs runs from overlapping push and PR events. - The License Compliance status reports 104 issues and has no result URL. The exact findings require owner/tool investigation; the failure must not be waived blindly. +- GitHub reports 42 open Dependabot vulnerabilities on the default branch (30 high, + 9 moderate, and 3 low at audit time). Most originate from the monolithic pinned + `requirements.txt`; one high-severity alert affects the SonarQube scan Action and + requires upgrading to a fixed release as part of Phase 0. - SonarQube dependency installation and coverage are marked best-effort, failures are suppressed, and `sonar.sources=.` makes source/test/notebook scope ambiguous. - The scan job can pass without proving that tests or coverage succeeded and does @@ -183,4 +187,3 @@ The original plans were directionally sound but incomplete. This review adds: - Greptile pricing: - conda-forge package contribution: - diff --git a/docs/development/urbanpy-0.3-project.md b/docs/development/urbanpy-0.3-project.md index 30c044f..01704c8 100644 --- a/docs/development/urbanpy-0.3-project.md +++ b/docs/development/urbanpy-0.3-project.md @@ -158,6 +158,10 @@ with documented `uv` commands. - Remove long-lived PyPI tokens; use a protected GitHub environment and OIDC trusted publishing with `id-token: write` only in the publish job. - Triage the current “License Compliance” failure instead of suppressing it. +- Resolve the 42 open Dependabot alerts observed at roadmap creation, prioritizing + the high-severity SonarQube scan Action alert and direct/runtime exposure. Do not + close alerts merely by deleting a manifest until replacement resolution is + locked, installed, tested, and rescanned. - Generate a dependency inventory and CycloneDX SBOM from the `uv` resolution. - Scan locked dependencies for known vulnerabilities and define severity/SLA and exception-expiry policy. @@ -319,6 +323,8 @@ Required before new architecture work is merged: - SonarQube remains operational and is no longer best-effort; - default-branch rules require human review and successful checks; - current license-compliance failure has an owner and documented disposition; +- no known high-severity dependency/workflow alert lacks a patched resolution or + time-bounded EL-BID-approved exception; - old issues and PRs are triaged into the Project. ### Phase 1 — 0.3.0a1 foundation @@ -429,4 +435,3 @@ Before an issue moves to Ready it has: installation are verified before trusted publication. - Documentation, citation, governance, security, support, and release ownership are current, and no P0/P1 issue or unowned high risk remains. -