Files
osmWorkflow/.trellis/tasks/archive/2026-08/08-11-asset-package-contract/design.md

6.5 KiB

Asset Package Contract Design

Product Boundary

outputs/<area-id>/ remains the build workspace. Its package/ child is the only publishable subtree and may be copied unchanged to another project. The package has no dependency on the parent directory, repository checkout, absolute local paths, QGIS, Blender, or the Cesium preview.

outputs/<area-id>/
  package/                         # publishable boundary
    manifest.json                   # asset-package/v1
    models/
      <area-id>.glb                 # compressed complete static scene
      roads.glb
      buildings.glb
      vegetation.glb
      water.glb
  <area-id>-cesium-preview.html     # local verification only
  _preview/                         # vehicles, routes, dynamic signal runtime
  _pipeline/                        # staging, manifests and build diagnostics
  osm2streets_web_out/, *.gpkg, *.qgz, *.blend, *.png

The package does not duplicate the GLBs: Cesium export and compression target the package model paths. Build staging lives under _pipeline/ and is removed before a successful package is published.

Manifest v1

The JSON root uses schema: "osm-asset-package/v1". Required top-level fields are:

Field Contract
schema, packageVersion, areaId Stable identity and schema evolution point
coordinateSystem Literal local ENU contract: X east, Y north, Z up, meters
placement WGS84 anchor longitude/latitude/height plus heading correction degrees
bounds WGS84 minLon, minLat, maxLon, maxLat
assets Deterministic list of declared publishable assets
sceneStats Optional descriptive OSM-derived counts, not loading-critical

Each asset has id, role, category, uri, and defaultLoad. uri is a forward-slash relative path inside package/; it must not be absolute, start with /, contain .., or resolve outside the package. Roles are scene for the complete static scene and layer for separately loadable semantic subsets. Categories are fixed in v1: scene, roads, buildings, vegetation, water.

models/<area-id>.glb is the only scene entry and defaults to load. Layer GLBs are optional alternatives for selective loading and default off; consumers must not load both the complete scene and overlapping layers unless intentionally composing them.

The manifest deliberately excludes source_osm, source_geojson, arbitrary Cesium JavaScript, vehicle routes, vehicle models, dynamic signal states, countdown models, preview runtime paths, build timestamps and desktop paths. File digests and sizes may be added under an optional integrity object only after they are computed from final compressed files.

Pipeline Design

1. Static export seed

blender/export_cesium.py continues to own static GLB generation, WGS84 bounds, anchor and semantic collection selection. It must emit data suitable for a manifest seed, not a preview metadata document. Static traffic-signal geometry already in the main scene remains part of the static scene; animated traffic signal and countdown collections are not package assets.

2. Package staging and compression

The build orchestrator owns output paths through normalizeAreaConfig(). It adds explicit package paths and exports static GLBs to a package staging directory beneath _pipeline/. Compression works only on staged package files and rewrites the staged manifest's main GLB URI after successful compression. It never needs to copy or alter preview HTML.

After path validation, file existence checks, and manifest validation, the package stage promotes the whole staging directory to outputs/<area-id>/package/. Promotion is directory-level and replaces a previous package only after the new one is valid. On failure, the previous published package remains usable and staging is retained or reported for diagnosis according to existing pipeline failure conventions.

3. Preview adapter

The existing Cesium preview remains a verification tool outside the package. It reads the package manifest via its relative path, then loads preview-only route, vehicle and dynamic-signal descriptors from _preview/. Dynamic traffic assets are declared in a preview-specific descriptor, never appended to package assets. This preserves existing high-precision cruise validation without making it a downstream requirement.

4. Stage and compatibility behavior

Canonical order becomes intermediates/reimport -> blender -> cesium -> compress -> package -> preview. package is included in the full default build and is callable explicitly to validate/publish existing staged static exports. preview stays an optional/verification stage, not a package dependency.

Existing npm run build:area -- --config ... --stages ... remains supported. Root level legacy GLB/JSON/HTML files are not deleted by the migration; they are not read as a fallback by the new package contract. A clean full build produces the package as the canonical downstream asset source.

Consumer Examples

Examples live in repository source, outside individual packages:

  • Cesium: fetch manifest.json, derive the ENU frame from placement, apply heading correction, then load assets by uri.
  • Three.js: load the selected model URL relative to the manifest URL; expose the same ENU placement object to the host application's georeferencing adapter. It must not imply that Three.js alone converts ENU to WGS84.

Both examples load the complete scene and demonstrate selecting a single layer. They validate that URLs are resolved relative to the manifest rather than the current page or repository root.

Validation and Rollback

Unit tests validate schema fields, category/role legality, package-relative URI rules, and rejection of omitted/extra files. An integration fixture constructs a minimal package and verifies that Cesium and Three.js example resolvers choose identical relative URLs and placement values. A target-area package test checks final compressed GLB parsing and manifest references.

The package stage writes a stage manifest containing only final published records and their integrity. Diagnostic checks distinguish missing package, invalid package, and stale package. Rollback is to the prior package/ directory; legacy preview outputs remain untouched throughout rollout.

Deferred Decisions

  • Rich per-feature semantics, routing graph delivery and simulation inputs are not in v1; they should be introduced through a later schema version only when a downstream consumer requires them.
  • Asset coverage improvements are downstream of this contract work.