oswm_codebase

OSWM global-launch readiness

Decision

The shared codebase is ready to support a controlled Milan pilot, but a many-node global launch should wait for hosted cold-start, no-change, and incremental evidence from Milan and at least two additional, operationally different nodes.

This branch closes the repository-safety and reproducibility blockers found in the opensidewalkmap_beta template. It does not claim that best-effort public data providers can sustain simultaneous global cold starts.

What was stressed

The investigation started from a shallow clone of the reference node and changed only its node identity to Milan. That exposed two separate classes of risk:

  1. A config-only clone retained hundreds of Curitiba-generated files, including boundaries, raw and processed data, PMTiles, update state, quality reports, statistics, API pages, and embedded URLs. The Milan branch removed 369 tracked generated files (about 302 MB) before testing.
  2. The automation could report success after failed setup commands, replace the entire node workflow directory, stage the whole checkout, pull an unreviewed latest submodule revision, and collide across scheduled writer jobs. Pages publication was implicit or absent, and timestamps mixed local wall time with UTC interpretation.

The Milan boundary resolves to OSM administrative relation 44915, with the configured fallback bounds (45.3867381, 9.0408867, 45.5358482, 9.2781103). An earlier public-provider probe showed the full Milan OSWM tag union timing out at one public Overpass endpoint; even a highway-only subset returned about 70,000 elements. Fleet enrollment therefore needs staggered jobs and, beyond the pilot, controlled bulk/Overpass capacity.

Implemented contracts

Clean node initialization and stale-output removal

node_outputs.py is the canonical generated-output contract.

# Dry-run: list inherited generated paths without changing them.
python oswm_codebase/node_outputs.py --root . reset-node

# Apply only when deliberately turning a template into a new node.
python oswm_codebase/node_outputs.py --root . reset-node --apply

# Reconcile all derived products before a complete regeneration.
python oswm_codebase/node_outputs.py --root . reset-derived

# Enforce the deployment-product contract and GitHub size guard.
python oswm_codebase/node_outputs.py --root . manifest
python oswm_codebase/node_outputs.py --root . require
python oswm_codebase/node_outputs.py --root . validate-sizes --max-mib 95

Both reset commands refuse filesystem roots and directories that do not look like OSWM nodes. Initialization preserves README.md, index.html, config.py, Git metadata, and the oswm_codebase gitlink, then recreates only an empty update registry. Derived reconciliation preserves the weekly quality_check/keys_without_wiki.json input while deleting undeclared products from complete-rebuild directories.

Reproducible node identity and provider behavior

Deterministic pipeline state

pipeline_decision.py emits JSON with one of three modes:

OSWM_FORCE_REGEN is wired into the daily runner. A codebase revision change forces a derived rebuild even when the OSM watcher reports no changes. The daily runner records success only after every stage and every required output passes.

Registry writes use timezone-aware UTC ISO-8601 strings. Existing DD/MM/YYYY HH:MM:SS values are interpreted in METADATA_TIMEZONE (UTC when unspecified) and remain readable during migration.

The manifest includes every public entry page, including acquisition, API, quality, statistics, and watcher pages. Full rebuilds re-render the watcher dashboard and feeds after derived-output cleanup, and the final require gate fails the run when any declared page or feed is missing or empty.

Safe fleet automation

Reproducible runtime and CI

requirements.txt is an exact, universal Python 3.12 lock generated from requirements.in; the development lock adds pytest and PyYAML. Core CI runs the Python suite, browser-module tests under Node, shell parsing, and package consistency checks.

Current local evidence on this branch:

These are contract and fixture tests. They are intentionally not presented as a successful live Milan generation.

Enrolling Milan (or another new node)

  1. Shallow-clone the reference node without trusting its generated state.
  2. Create an isolated branch and initialize the recorded submodule gitlink.
  3. Run the dry reset, inspect the listed paths, then apply it.
  4. Set at least CITY_NAME, CITY_SHORTNAME, REPO_NAME, bounding box, midpoint, OSM_RELATION_ID, NODE_DAILY_CRON, and NODE_WEEKLY_CRON.
  5. Pin a core revision that passed core CI; do not configure the submodule to float automatically.
  6. Run python oswm_codebase/special_updates.py and review the workflow/state diff.
  7. Run core tests and the node readiness audit before any generation.
  8. Run one read-only hosted cold start with logs and resource metrics. Do not push its output until the required-output, PMTiles, stale-output, and 95 MiB gates pass.
  9. Run an immediate second cycle and require the machine decision to be skip. Then test a known small incremental fixture/change.
  10. Visually verify the deployed homepage, MapLibre themes, charts, printable snapshot, routing, hazard views, quality pages, statistics, metadata, and static API.

Remaining launch gates

Priority 0 for Milan:

Priority 0 before many nodes:

Priority 1:

Launch criterion

Milan is accepted only when cold, no-change, and incremental hosted runs pass, obsolete sentinels are removed, the exact core SHA is visible in node state, no history is rewritten, Pages is verified, and provider failure yields a bounded actionable error rather than a false success. Global enrollment begins only after the same contract passes on multiple staggered pilots.