# Asset Package Contract Design ## Product Boundary `outputs//` 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// package/ # publishable boundary manifest.json # asset-package/v1 models/ .glb # compressed complete static scene roads.glb buildings.glb vegetation.glb water.glb -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/.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//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.