From 4e9c274174af27416b3ede6baf96334e9ab01fc9 Mon Sep 17 00:00:00 2001 From: snkmcb Date: Sat, 22 Aug 2026 20:54:18 +0900 Subject: [PATCH 1/2] refactor(resolver): relocate httpresolver test double out of product surface The repository-local HTTP resolver was shipped under plugins/ despite being an integration-test double, which put it in the product surface, the release matrix, and its own CI cells. Move it to tests/plugins/httpresolver and drop it from the product build: remove the top-level add_subdirectory, its release metadata assertions, and its standalone CI cells. The COPC Tier 1 integration test now builds it transitively under USDGEO_BUILD_TESTS, keeping the required gate independent of external resolver repositories. Its openstrata.plugin.yaml is removed since it is no longer a published plugin. Add TryBuildPointCloudCacheLayout so producer and consumer tests derive resolver-backed cache entries from the same descriptor contract, and rework the resolver cache Tier 1 coverage to assert hits, incomplete and corrupted entry invalidation, and validation-token changes through cache artifacts rather than process-local counters, which are not shared across the FileFormat DLL boundary on Windows. Co-Authored-By: Claude Opus 5 --- .github/workflows/ost-source-ci.yml | 54 ------- .gitignore | 1 + CHANGELOG.md | 34 +++-- CMakeLists.txt | 1 - README.md | 13 +- docs/architecture/RESOLVER_SOURCE.md | 24 ++-- docs/architecture/WORKSPACE.md | 16 +-- docs/compatibility/OPENUSD.md | 14 +- docs/guides/INSTALL.md | 18 +-- docs/releases/README.md | 6 +- docs/roadmap/implementation-status.md | 10 +- docs/roadmap/infrastructure-maturity.md | 25 ++-- libs/usd-pointcloud-authoring/README.md | 2 + .../include/usdgeo/PointCloudCache.h | 11 +- .../src/PointCloudCache.cpp | 24 +++- .../tests/test_pointcloud_cache.cpp | 14 +- openstrata.ci.yaml | 42 +----- plugins/httpresolver/openstrata.plugin.yaml | 15 -- plugins/pointcloud-copc/CMakeLists.txt | 14 +- plugins/pointcloud-copc/README.md | 2 +- .../tests/test_pointcloud_copc.cpp | 136 +++++++++++++----- .../plugins}/httpresolver/CMakeLists.txt | 9 +- .../plugins}/httpresolver/README.md | 17 ++- .../resources/httpresolver/plugInfo.json | 0 .../resources/httpresolver/plugInfo.json.in | 0 .../httpresolver/src/HttpResolver.cpp | 0 tools/check_release_metadata.py | 6 +- 27 files changed, 250 insertions(+), 258 deletions(-) delete mode 100644 plugins/httpresolver/openstrata.plugin.yaml rename {plugins => tests/plugins}/httpresolver/CMakeLists.txt (79%) rename {plugins => tests/plugins}/httpresolver/README.md (51%) rename {plugins => tests/plugins}/httpresolver/plugin/resources/httpresolver/plugInfo.json (100%) rename {plugins => tests/plugins}/httpresolver/plugin/resources/httpresolver/plugInfo.json.in (100%) rename {plugins => tests/plugins}/httpresolver/src/HttpResolver.cpp (100%) diff --git a/.github/workflows/ost-source-ci.yml b/.github/workflows/ost-source-ci.yml index cadbc84..e81134d 100644 --- a/.github/workflows/ost-source-ci.yml +++ b/.github/workflows/ost-source-ci.yml @@ -200,60 +200,6 @@ jobs: host_python: "3.13" host_packages_apt: "libx11-dev libxt-dev libxext-dev libgl1-mesa-dev" host_packages_brew: "" - - name: httpresolver-pr-windows - lane: pull_request - runtime_artifact: sha256:c3ed40122756ea118166e1619efcaec463e7d2a42f6d978fe4e20b6b774c4b03 - target_trust: local - minimum_trust: local - require_evidence: all - evidence_flags: "--require-sbom --require-provenance" - platform: cy2026 - profile: usd - up_to: 4 - runs_on: ["windows-2022"] - hosted: true - runner_profile: windows-hosted - bundle: plugins/httpresolver - runtime_remote: "oci://ghcr.io/animu-sphere/openstrata-runtime-cy2026-usd@sha256:07cb84412017ece911adaed1175a6373865d863bc40124081cfc20c52de7f0d9" - host_python: "" - host_packages_apt: "" - host_packages_brew: "" - - name: httpresolver-pr-macos-arm64 - lane: pull_request - runtime_artifact: sha256:a9bb847ab5c7eb29d7425ff9acfb05b01c1751054d6a70628e48b06f8409a4a8 - target_trust: local - minimum_trust: local - require_evidence: all - evidence_flags: "--require-sbom --require-provenance" - platform: cy2026 - profile: usd - up_to: 5 - runs_on: ["macos-15"] - hosted: true - runner_profile: macos-arm64-hosted - bundle: plugins/httpresolver - runtime_remote: "oci://ghcr.io/animu-sphere/openstrata-runtime-cy2026-usd@sha256:dc0c980b868f91fe33e6f35a67a2bc5693b89f0243141c55ecb068ab225b0f3e" - host_python: "3.13" - host_packages_apt: "" - host_packages_brew: "" - - name: httpresolver-pr-linux - lane: pull_request - runtime_artifact: sha256:03f7d4ef263abb50511d17237e7cbdbe1b83dee02cff8b8f901f0484c7a7898e - target_trust: local - minimum_trust: local - require_evidence: all - evidence_flags: "--require-sbom --require-provenance" - platform: cy2026 - profile: usd - up_to: 5 - runs_on: ["ubuntu-24.04"] - hosted: true - runner_profile: linux-hosted - bundle: plugins/httpresolver - runtime_remote: "oci://ghcr.io/animu-sphere/openstrata-runtime-cy2026-usd@sha256:367a32bd1985ca9b07e8f7e64f95c1ded8dba16d68f17e0bffb09cec0b9dc3f6" - host_python: "3.13" - host_packages_apt: "libx11-dev libxt-dev libxext-dev libgl1-mesa-dev" - host_packages_brew: "" - name: pointcloud-ply-pr-windows lane: pull_request runtime_artifact: sha256:c3ed40122756ea118166e1619efcaec463e7d2a42f6d978fe4e20b6b774c4b03 diff --git a/.gitignore b/.gitignore index e8663eb..e1c9efc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ **/build/ **/.strata/ plugins/*/lib/ +tests/plugins/*/lib/ CMakeUserPresets.json # CI and release scratch. diff --git a/CHANGELOG.md b/CHANGELOG.md index b7ad0fc..72feb1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,12 +16,12 @@ All notable changes to this project are documented here. no-secrets rule, and the Tier 1 / Tier 2 test split. - Stated that no resolver implementation is a build-time dependency, and that `usd-http-resolver` is one compatible implementation composed at runtime. -- Marked `plugins/httpresolver` explicitly as an integration-test double whose - removal or relocation to a test-only path is v0.10.0 work. -- Staged v0.10.0 around the pre-implementation `usd-http-resolver` repository: - repository-local identity and cache tests proceed first, while external - interoperability and test-double disposition wait for its first released - resolver implementation. +- Relocated the repository-local resolver test double to + `tests/plugins/httpresolver` and documented that it is excluded from the + product surface and release matrix. +- Updated the Tier 2 plan for the released + [`usd-http-resolver`](https://github.com/animu-sphere/usd-http-resolver) + implementation and its resolver-neutral `ArAssetInfo` identity contract. - Added an OpenStrata 0.22.2 dogfooding record for the external resolver skeleton; it identifies repository setup work, not an OpenStrata defect. @@ -32,9 +32,25 @@ All notable changes to this project are documented here. - Added resolver identity conversion tests and cache-key invalidation coverage for changed opaque validation tokens. -The OpenUSD-facing resolver adapter is now centralized in the shared authoring -cache bridge. Complete generated-cache generation, diagnostics, and external -resolver interoperability remain planned. +The OpenUSD-facing resolver adapter is centralized in the shared authoring +cache bridge. Stable-identity generated-cache reuse and recovery are complete; +diagnostic completion and recorded external interoperability remain planned. + +### Changed + +- Removed standalone `httpresolver` product CI cells. The relocated test double + is built transitively by the COPC Tier 1 integration test, keeping the + required gate independent of external resolver repositories. +- Added a shared cache-layout construction entry point so producer and + consumer tests derive resolver-backed cache entries from the same descriptor + contract. + +### Fixed + +- Resolver cache Tier 1 coverage now verifies cache hits, incomplete and + corrupted entry invalidation, and validation-token changes through cache + artifacts instead of process-local counters that are not shared across a + FileFormat DLL boundary on Windows. ## [0.9.0] - 2026-08-15 diff --git a/CMakeLists.txt b/CMakeLists.txt index 6fa32c4..5d7778f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,7 +42,6 @@ if(USDGEO_BUILD_USD) add_subdirectory(tools/usd-pointcloud-convert) add_subdirectory(plugins/pointcloud-las) add_subdirectory(plugins/pointcloud-laz) - add_subdirectory(plugins/httpresolver) add_subdirectory(plugins/pointcloud-copc) add_subdirectory(plugins/pointcloud-ply) endif() diff --git a/README.md b/README.md index d62196a..ccf49e2 100644 --- a/README.md +++ b/README.md @@ -76,11 +76,12 @@ the selected resolver implementation, not by this repository. The plugins consume the resolved `ArAsset` and never learn which resolver opened it. An external resolver is runtime composition: install it alongside these -bundles and register both through `PXR_PLUGINPATH_NAME`. `usd-http-resolver` is -one compatible implementation, not a required dependency — this repository -builds and tests without it. The bundled `plugins/httpresolver` is an -integration-test double that serves a local fixture in memory; it is not a -network transport. +bundles and register both through `PXR_PLUGINPATH_NAME`. +[`usd-http-resolver`](https://github.com/animu-sphere/usd-http-resolver) is one +compatible implementation, not a required dependency — this repository builds +and tests without it. The repository-local +`tests/plugins/httpresolver` fixture is a Tier 1 test double that serves a local +fixture in memory; it is not a network transport or product bundle. Generated-USDC cache reuse for resolver-backed sources requires stable source identity and stays disabled when the resolver cannot supply it. The boundary, @@ -349,7 +350,7 @@ plugins/pointcloud-las/ LAS OpenUSD FileFormat Plugin plugins/pointcloud-laz/ LAZ OpenUSD FileFormat Plugin plugins/pointcloud-copc/ COPC OpenUSD FileFormat Plugin plugins/pointcloud-ply/ PLY OpenUSD FileFormat Plugin -plugins/httpresolver/ Resolver test double for the COPC integration test, not a transport +tests/plugins/httpresolver/ Tier 1 resolver fixture, excluded from product discovery docs/ See docs/README.md for the documentation index ``` diff --git a/docs/architecture/RESOLVER_SOURCE.md b/docs/architecture/RESOLVER_SOURCE.md index 2cf8328..e38f35d 100644 --- a/docs/architecture/RESOLVER_SOURCE.md +++ b/docs/architecture/RESOLVER_SOURCE.md @@ -241,7 +241,7 @@ pointcloud-copc `usd-http-resolver` is one compatible implementation, not a required dependency. Registration is in [INSTALL.md](../guides/INSTALL.md). -## 6. Testing tiers — Planned (`v0.10.0`) +## 6. Testing tiers — In progress (`v0.10.0`) **Tier 1 — repository-local contract tests.** They run with no external resolver repository, using fake or memory-backed test assets, and remain the @@ -259,21 +259,23 @@ and tested by the resolver repository. Tier 2 is reproducible through OpenStrata workspace composition without making this repository structurally dependent on the resolver repository. -`usd-http-resolver` is the designated first Tier 2 implementation, but is -pre-implementation as of 2026-08-16: it has no resolver bundle, backend, cache, -or release yet. Therefore Tier 1 is the v0.10.0 implementation gate in this -repository. Tier 2 becomes a release gate only after that repository publishes -its first resolver implementation and its OpenStrata build and test workflow. +[`usd-http-resolver`](https://github.com/animu-sphere/usd-http-resolver) is the +designated first Tier 2 implementation. Its `v0.2.0` release provides the HTTP +backend and OpenUSD resolver bundle, exposes stable resolver-neutral identity +through `ArAssetInfo`, and is tested through its own OpenStrata workflow. Tier +1 remains this repository's required CI gate; Tier 2 is now ready to be +composed and recorded as the `v0.10.0` release gate. ## 7. Test-double resolver -`plugins/httpresolver` is an integration-test double: it serves a configured +`tests/plugins/httpresolver` is an integration-test fixture: it serves a configured local fixture as an in-memory `ArAsset` for `http://memory.copc` and `https://memory.copc`. It is not a network transport and is not part of the point-cloud product surface. -It remains in place until equivalent external integration coverage exists. -After that coverage is recorded, `v0.10.0` either removes it or relocates it -under an explicitly test-only path such as `tests/plugins/httpresolver/`. It -must not be presented as equivalent to a production point-cloud bundle. See +The test double remains as the external-dependency-free Tier 1 fixture. It is +built only with the COPC integration tests and has no product bundle manifest +or install rule, so it is excluded from plugin discovery, release metadata, +packaging, and standalone CI cells. It must not be presented as equivalent to +a production resolver bundle. See [WORKSPACE.md](WORKSPACE.md). diff --git a/docs/architecture/WORKSPACE.md b/docs/architecture/WORKSPACE.md index 5f709fc..91ccfc8 100644 --- a/docs/architecture/WORKSPACE.md +++ b/docs/architecture/WORKSPACE.md @@ -28,7 +28,7 @@ modules implement is fixed in | `pointcloud-laz` | `plugins/pointcloud-laz` | OpenStrata plugin bundle (`usd-fileformat`) | implemented | The same adapter shape for `.laz`, using `LazReader` and the laz-perf codec integration. Owns its `LAZxxx` diagnostic codes. | | `usdCopc` | `libs/usd-copc` | plain CMake/OpenStrata static library | implemented (source-backed) | COPC Info and hierarchy validation, project-owned random-access source integration, selective point-data range decoding through the shared LAZ chunk decoder, and native hierarchy streaming. It remains OpenUSD- and transport-independent. | | `pointcloud-copc` | `plugins/pointcloud-copc` | OpenStrata plugin bundle (`usd-fileformat`) | implemented (resolver-backed read) | Resolver-opened `ArAsset` adaptation, metadata-only and non-tiled reads, and native hierarchy tiled COPC authoring through shared `usdLod`. Remote tiled reads require a local payload directory; source point ranges remain unsupported. | -| `httpresolver` | `plugins/httpresolver` | OpenStrata plugin bundle (`usd-asset-resolver`) | test-only, disposition pending in `v0.10.0` | OpenUSD `ArResolver` bundle for `http://memory.copc` and `https://memory.copc`, serving a configured local fixture as an in-memory `ArAsset` for resolver-backed COPC integration tests. It implements no network transport and is not part of the product surface. `v0.10.0` either removes it once equivalent external integration coverage exists, or relocates it to an explicitly test-only path such as `tests/plugins/httpresolver/`. | +| `httpresolver` | `tests/plugins/httpresolver` | test-only OpenUSD `ArResolver` fixture | Tier 1 fixture | OpenUSD `ArResolver` test double for `http://memory.copc` and `https://memory.copc`, serving a configured local fixture as an in-memory `ArAsset`. It has no network transport or product bundle manifest, is built only with COPC integration tests, and is excluded from product discovery and release matrices. | | `usdPointCloudTiling` | `libs/usd-pointcloud-tiling` | plain CMake/OpenStrata static library | implemented | Format-independent fixed-grid partitioning, spill-backed bounded-memory routing, deterministic tile and LOD ordering, validated tile manifest serialization, spool validation, and cleanup contracts. See the [streaming and tiling plan](../roadmap/streaming-and-tiling.md). | | `usdGeoCache` | `libs/usd-geo-cache` | plain CMake/OpenStrata static library | implemented | Descriptor-based stable cache keys, deterministic USDC root/payload layout, machine-readable lookup states, process-local lookup statistics, and entry invalidation. The conversion tool owns generation and atomic publication; direct FileFormat adapters reuse committed entries through `USDGEO_CACHE_ROOT`. | | `usdPly` | `libs/usd-ply` | plain CMake/OpenStrata static library | implemented | PLY 1.0 header inspection, scalar vertex decoding, source filters, and explicit georeference conversion into shared point-cloud assets. | @@ -267,7 +267,8 @@ Every structural or format change preserves these invariants: `openstrata.ci.yaml` is the source of truth; the GitHub workflow is generated by `ost ci generate github`. The declared PR matrix runs every production -bundle on every host and validates the HTTP resolver bundle separately: +bundle on every host. The Tier 1 resolver test double is built transitively by +the COPC integration test and has no product-bundle cell: | Host | Target | OST level | | --- | --- | --- | @@ -284,19 +285,16 @@ ost test ost plugin build plugins/pointcloud-las ost plugin build plugins/pointcloud-laz ost plugin build plugins/pointcloud-copc -ost plugin build plugins/httpresolver ost plugin test plugins/pointcloud-las --up-to 4 ost plugin test plugins/pointcloud-laz --up-to 4 ost plugin test plugins/pointcloud-copc --up-to 4 -ost plugin test plugins/httpresolver --up-to 4 ``` The LAS, LAZ, COPC, and PLY bundles declare OST smoke fixtures and run the L3 -`usdcat.read` and L4 `python.stage_open` checks. The `httpresolver` bundle has -no standalone fixture because its functional path is exercised by the COPC -integration test; its PR cells validate resolver-bundle structure, runtime -compatibility, and plugin discovery. The COPC bundle follows the same runtime -matrix as LAS and LAZ. +`usdcat.read` and L4 `python.stage_open` checks. The test-only `httpresolver` +bundle has no standalone fixture or CI matrix cell because its functional path +is exercised by the COPC Tier 1 integration test. The COPC bundle follows the +same runtime matrix as LAS and LAZ. The gate must stay passable without any external resolver repository. From `v0.10.0`, repository-local resolver contract tests (Tier 1) are the required diff --git a/docs/compatibility/OPENUSD.md b/docs/compatibility/OPENUSD.md index 9d99a24..3a3331f 100644 --- a/docs/compatibility/OPENUSD.md +++ b/docs/compatibility/OPENUSD.md @@ -13,10 +13,11 @@ runtime: ``` `pointcloud-las`, `pointcloud-laz`, `pointcloud-copc`, and `pointcloud-ply` are -`usd-fileformat` bundles and require the `usd-stage-read` capability. -`httpresolver` is a `usd-asset-resolver` test-double bundle for the resolver -path used by COPC integration tests. It is not part of the product surface and -provides no network transport; a production resolver is a separate project. +`usd-fileformat` bundles and require the `usd-stage-read` capability. The +`tests/plugins/httpresolver` fixture is an external-dependency-free Tier 1 test +double, not part of the product surface. Production network transport is +provided by a separately installed resolver such as +[`usd-http-resolver`](https://github.com/animu-sphere/usd-http-resolver). | Item | Value | | --- | --- | @@ -134,8 +135,9 @@ remain static `SDF_FORMAT_ARGS`. See [ADR-0003](../adr/0003-dynamic-file-format. detect which resolver opened an asset. `usd-http-resolver` is one compatible implementation; register it alongside these bundles through `PXR_PLUGINPATH_NAME`. -- The repository's `httpresolver` bundle is an integration-test double only; - it does not provide network transport or production HTTP behavior. +- The repository's `tests/plugins/httpresolver` fixture is a Tier 1 integration- + test double only; it does not provide network transport or production HTTP + behavior and is excluded from the product matrix. The boundary is stated in full in the [resolver-backed source contract](../architecture/RESOLVER_SOURCE.md). diff --git a/docs/guides/INSTALL.md b/docs/guides/INSTALL.md index eb81231..6f4deea 100644 --- a/docs/guides/INSTALL.md +++ b/docs/guides/INSTALL.md @@ -61,16 +61,17 @@ transport, authentication, retries, and raw byte caching belong to the resolver implementation, not to these plugins, so remote sources need a resolver that resolves the identifier and serves an `ArAsset` with efficient range reads. -An external resolver is runtime composition. Install it beside the point-cloud -bundles and register both: +An external resolver is runtime composition. For example, build or install +[`usd-http-resolver`](https://github.com/animu-sphere/usd-http-resolver), place +its bundle beside `pointcloud-copc`, and register both resource roots: ```powershell $env:PXR_PLUGINPATH_NAME = - "C:\path\to\pointcloud-copc\plugin\resources\;C:\path\to\resolver\plugin\resources\" + "C:\path\to\pointcloud-copc\plugin\resources\;C:\path\to\http-resolver\plugin\resources\httpResolver" ``` ```bash -export PXR_PLUGINPATH_NAME=/path/to/pointcloud-copc/plugin/resources/:/path/to/resolver/plugin/resources/ +export PXR_PLUGINPATH_NAME=/path/to/pointcloud-copc/plugin/resources/:/path/to/http-resolver/plugin/resources/httpResolver ``` OpenUSD then composes them: @@ -80,10 +81,11 @@ usdview https://example.org/data.copc -> ArResolver -> external resolver -> ArAsset -> pointcloud-copc ``` -`usd-http-resolver` is one compatible implementation. It is not required, and -these bundles are built and tested without it. The repository's -`plugins/httpresolver` bundle is an integration-test double that serves a local -fixture in memory; do not deploy it as a transport. See the +`usd-http-resolver` is optional and is never linked by these bundles. Its +`ArAsset::Read` implementation supplies bounded remote reads, while its +transport cache remains separate from the generated-USDC cache owned here. The +repository's `tests/plugins/httpresolver` fixture remains only as an external- +dependency-free Tier 1 test double; do not deploy it as a transport. See the [resolver-backed source contract](../architecture/RESOLVER_SOURCE.md). Generated-USDC cache reuse for resolver-backed sources requires stable source diff --git a/docs/releases/README.md b/docs/releases/README.md index 9c5068e..fb35a26 100644 --- a/docs/releases/README.md +++ b/docs/releases/README.md @@ -69,10 +69,8 @@ Run these steps from the repository root before creating the release tag: `plugins/pointcloud-laz/CMakeLists.txt`, `plugins/pointcloud-copc/openstrata.plugin.yaml`, `plugins/pointcloud-copc/CMakeLists.txt`, - `plugins/pointcloud-ply/openstrata.plugin.yaml`, - `plugins/pointcloud-ply/CMakeLists.txt`, - `plugins/httpresolver/openstrata.plugin.yaml`, and - `plugins/httpresolver/CMakeLists.txt`. + `plugins/pointcloud-ply/openstrata.plugin.yaml`, and + `plugins/pointcloud-ply/CMakeLists.txt`. 2. Run `python tools/check_release_metadata.py` to verify that all package version declarations match `VERSION`. 3. Update `CHANGELOG.md`, the release record, and any capability or diff --git a/docs/roadmap/implementation-status.md b/docs/roadmap/implementation-status.md index f086506..3b7598c 100644 --- a/docs/roadmap/implementation-status.md +++ b/docs/roadmap/implementation-status.md @@ -339,13 +339,13 @@ the typed `COPC009` diagnostic while preserving the conservative fallback. Phase 4 — repository boundary cleanup: -- [ ] Remove `plugins/httpresolver`, or relocate it to an explicitly test-only - path such as `tests/plugins/httpresolver/` -- [ ] Remove documentation language implying this repository owns a production +- [x] Relocate `plugins/httpresolver` to the explicitly test-only + `tests/plugins/httpresolver/` path +- [x] Remove documentation language implying this repository owns a production HTTP implementation -- [ ] Document the external resolver contract and `usd-http-resolver` +- [x] Document the external resolver contract and `usd-http-resolver` interoperability guidance as optional runtime composition -- [ ] Verify the repository builds and tests with no CMake dependency, +- [x] Verify the repository builds and tests with no CMake dependency, submodule, vendored HTTP library, or link dependency on a resolver implementation diff --git a/docs/roadmap/infrastructure-maturity.md b/docs/roadmap/infrastructure-maturity.md index 45c9452..9ad4957 100644 --- a/docs/roadmap/infrastructure-maturity.md +++ b/docs/roadmap/infrastructure-maturity.md @@ -431,9 +431,9 @@ resolver-specific include, or link dependency on any resolver implementation. `usd-http-resolver` is documented as one compatible implementation composed at runtime through `PXR_PLUGINPATH_NAME`, never as a requirement. -`plugins/httpresolver` stops being presented as part of the product surface. It -is removed once equivalent external integration coverage exists, or relocated -under an explicitly test-only path such as `tests/plugins/httpresolver/`. +The memory-backed resolver fixture lives under +`tests/plugins/httpresolver`, is built only for Tier 1 COPC integration tests, +and is excluded from the product plugin and release matrices. #### Tests @@ -448,13 +448,12 @@ COPC fixture through OpenStrata workspace composition, and verifies resolution, metadata and range reads, local/remote output equivalence, reuse under stable identity, and invalidation when validation metadata changes. -`usd-http-resolver` is the intended first external implementation. Its -2026-08-16 snapshot is documentation and an OpenStrata project skeleton only; -it has no resolver bundle, backend, cache, registered tests, or release. The -release sequence is consequently staged: finish and gate Tier 1 here first, -then run and record Tier 2 after the resolver publishes its first implementation -and OpenStrata workflow. The bundled test double remains test-only until that -evidence supports its removal or relocation. +[`usd-http-resolver`](https://github.com/animu-sphere/usd-http-resolver) is the +first external implementation. Its `v0.2.0` release provides the HTTP backend, +OpenUSD resolver bundle, stable `ArAssetInfo` identity, and OpenStrata build and +test workflow. Tier 1 remains the dependency-free gate here; Tier 2 can now be +composed against the released resolver and recorded independently of this +repository's build graph. #### Diagnostics @@ -474,11 +473,9 @@ custom point-cloud USD schemas, and renderer-controlled runtime streaming. Exit gate: a documented identity contract for resolver-provided sources, reuse enabled exactly where identity is sufficient, Tier 1 passing without an -external resolver, the bundled test resolver clearly marked test-only, and +external resolver, the bundled test resolver isolated under `tests/`, and recorded remote baselines including `bytes fetched / source size`. Before the -release is tagged, Tier 2 must be recorded against a released external resolver; -that evidence then decides whether the bundled test resolver is removed or -relocated. +release is tagged, Tier 2 must be recorded against a released external resolver. ### Research - Runtime Streaming diff --git a/libs/usd-pointcloud-authoring/README.md b/libs/usd-pointcloud-authoring/README.md index 8941bf2..0b143cf 100644 --- a/libs/usd-pointcloud-authoring/README.md +++ b/libs/usd-pointcloud-authoring/README.md @@ -51,6 +51,7 @@ namespace `usdgeo`. ```text usdgeo/PointCloudLayer.h +usdgeo/PointCloudCache.h ``` | Group | Entry points | @@ -61,6 +62,7 @@ usdgeo/PointCloudLayer.h | LOD | `AuthorPointCloudLodAsset` | | Tiling | `PointCloudTileAsset`, `AuthorPointCloudTiledAsset` | | Payloads | `PointCloudPayloadOptions`, `AuthorPointCloudTiledAssetWithPayloads` | +| Generated cache | `TryBuildPointCloudCacheLayout`, `TryLoadPointCloudCache`, `TryBuildResolverSourceIdentity` | | Failure kinds | `PointCloudAuthorFailure` (`InvalidLayer`, `StageCreation`, `StageMetrics`, `PointCloud`) | Minimal use: diff --git a/libs/usd-pointcloud-authoring/include/usdgeo/PointCloudCache.h b/libs/usd-pointcloud-authoring/include/usdgeo/PointCloudCache.h index 2c1a43e..667f0aa 100644 --- a/libs/usd-pointcloud-authoring/include/usdgeo/PointCloudCache.h +++ b/libs/usd-pointcloud-authoring/include/usdgeo/PointCloudCache.h @@ -15,6 +15,15 @@ namespace usdgeo { std::filesystem::path PointCloudCacheRootFromEnvironment(); +bool TryBuildPointCloudCacheLayout( + const std::filesystem::path& cacheRoot, + const cache::SourceIdentity& sourceIdentity, + const GeoReference& reference, + const usdpointcloud::PointReadRequest& request, + const std::string& parserVersion, + cache::Layout& layout, + std::string& errorMessage); + bool TryBuildResolverSourceIdentity( const pxr::ArResolver& resolver, const std::string& assetPath, @@ -43,4 +52,4 @@ bool TryLoadPointCloudCache( bool& hit, std::string& errorMessage); -} // namespace usdgeo \ No newline at end of file +} // namespace usdgeo diff --git a/libs/usd-pointcloud-authoring/src/PointCloudCache.cpp b/libs/usd-pointcloud-authoring/src/PointCloudCache.cpp index 7ccaec9..fe96bf7 100644 --- a/libs/usd-pointcloud-authoring/src/PointCloudCache.cpp +++ b/libs/usd-pointcloud-authoring/src/PointCloudCache.cpp @@ -323,6 +323,28 @@ std::filesystem::path PointCloudCacheRootFromEnvironment() { return error ? std::filesystem::path{} : root.lexically_normal(); } +bool TryBuildPointCloudCacheLayout( + const std::filesystem::path& cacheRoot, + const cache::SourceIdentity& sourceIdentity, + const GeoReference& reference, + const usdpointcloud::PointReadRequest& request, + const std::string& parserVersion, + cache::Layout& layout, + std::string& errorMessage) { + cache::Descriptor descriptor; + if (!BuildDescriptor(sourceIdentity, reference, request, parserVersion, + descriptor, errorMessage)) { + layout = {}; + return false; + } + if (!cache::TryBuildLayout(cacheRoot, descriptor, layout)) { + errorMessage = "unable to build cache layout"; + return false; + } + errorMessage.clear(); + return true; +} + bool TryLoadPointCloudCache( pxr::SdfLayer* layer, const std::filesystem::path& sourcePath, @@ -427,4 +449,4 @@ bool TryLoadPointCloudCache( return true; } -} // namespace usdgeo \ No newline at end of file +} // namespace usdgeo diff --git a/libs/usd-pointcloud-authoring/tests/test_pointcloud_cache.cpp b/libs/usd-pointcloud-authoring/tests/test_pointcloud_cache.cpp index 0664d62..c1b2e1c 100644 --- a/libs/usd-pointcloud-authoring/tests/test_pointcloud_cache.cpp +++ b/libs/usd-pointcloud-authoring/tests/test_pointcloud_cache.cpp @@ -233,24 +233,24 @@ void TestPointCloudCacheMissAndMaterialization() { Check(errorMessage.empty()); Check(!std::filesystem::exists(layout.entryDirectory)); - const usdgeo::cache::SourceIdentity resolverIdentity{ + const usdgeo::cache::SourceIdentity incompleteResolverIdentity{ "https://memory.example/pointcloud.copc", 42, 0, "revision-a"}; const auto resolverDescriptor = - MakeDescriptor(resolverIdentity, reference, request); + MakeDescriptor(incompleteResolverIdentity, reference, request); usdgeo::cache::Layout resolverLayout; Check(usdgeo::cache::TryBuildLayout( cacheRoot, resolverDescriptor, resolverLayout)); std::filesystem::create_directories(resolverLayout.payloadDirectory); std::ofstream(resolverLayout.rootLayer, std::ios::binary) << "incomplete resolver root"; - const auto resolverLayer = pxr::SdfLayer::CreateNew( + const auto incompleteResolverLayer = pxr::SdfLayer::CreateNew( (testRoot / "resolver-incomplete.usda").string()); - Check(resolverLayer); + Check(incompleteResolverLayer); hit = true; errorMessage.clear(); Check(usdgeo::TryLoadPointCloudCache( - resolverLayer.operator->(), resolverIdentity, {}, reference, request, - "las-reader-1", hit, errorMessage)); + incompleteResolverLayer.operator->(), incompleteResolverIdentity, {}, + reference, request, "las-reader-1", hit, errorMessage)); Check(!hit); Check(errorMessage.empty()); Check(!std::filesystem::exists(resolverLayout.entryDirectory)); @@ -374,4 +374,4 @@ void TestPointCloudCacheMissAndMaterialization() { int main() { TestPointCloudCacheMissAndMaterialization(); return 0; -} \ No newline at end of file +} diff --git a/openstrata.ci.yaml b/openstrata.ci.yaml index 33a4592..877f5f3 100644 --- a/openstrata.ci.yaml +++ b/openstrata.ci.yaml @@ -194,46 +194,6 @@ cells: apt: [libx11-dev, libxt-dev, libxext-dev, libgl1-mesa-dev] up_to: 5 - - name: httpresolver-pr-windows - lane: pull_request - runner: windows-hosted - runtime_artifact: sha256:c3ed40122756ea118166e1619efcaec463e7d2a42f6d978fe4e20b6b774c4b03 - runtime_remote: - uri: oci://ghcr.io/animu-sphere/openstrata-runtime-cy2026-usd@sha256:07cb84412017ece911adaed1175a6373865d863bc40124081cfc20c52de7f0d9 - expected_oci_digest: sha256:07cb84412017ece911adaed1175a6373865d863bc40124081cfc20c52de7f0d9 - bundle: plugins/httpresolver - platform: cy2026 - profile: usd - up_to: 4 - - - name: httpresolver-pr-macos-arm64 - lane: pull_request - runner: macos-arm64-hosted - runtime_artifact: sha256:a9bb847ab5c7eb29d7425ff9acfb05b01c1751054d6a70628e48b06f8409a4a8 - runtime_remote: - uri: oci://ghcr.io/animu-sphere/openstrata-runtime-cy2026-usd@sha256:dc0c980b868f91fe33e6f35a67a2bc5693b89f0243141c55ecb068ab225b0f3e - expected_oci_digest: sha256:dc0c980b868f91fe33e6f35a67a2bc5693b89f0243141c55ecb068ab225b0f3e - bundle: plugins/httpresolver - platform: cy2026 - profile: usd - host_python: "3.13" - up_to: 5 - - - name: httpresolver-pr-linux - lane: pull_request - runner: linux-hosted - runtime_artifact: sha256:03f7d4ef263abb50511d17237e7cbdbe1b83dee02cff8b8f901f0484c7a7898e - runtime_remote: - uri: oci://ghcr.io/animu-sphere/openstrata-runtime-cy2026-usd@sha256:367a32bd1985ca9b07e8f7e64f95c1ded8dba16d68f17e0bffb09cec0b9dc3f6 - expected_oci_digest: sha256:367a32bd1985ca9b07e8f7e64f95c1ded8dba16d68f17e0bffb09cec0b9dc3f6 - bundle: plugins/httpresolver - platform: cy2026 - profile: usd - host_python: "3.13" - host_packages: - apt: [libx11-dev, libxt-dev, libxext-dev, libgl1-mesa-dev] - up_to: 5 - - name: pointcloud-ply-pr-windows lane: pull_request runner: windows-hosted @@ -432,4 +392,4 @@ cells: host_python: "3.13" host_packages: apt: [libx11-dev, libxt-dev, libxext-dev, libgl1-mesa-dev] - up_to: 5 \ No newline at end of file + up_to: 5 diff --git a/plugins/httpresolver/openstrata.plugin.yaml b/plugins/httpresolver/openstrata.plugin.yaml deleted file mode 100644 index 97185b6..0000000 --- a/plugins/httpresolver/openstrata.plugin.yaml +++ /dev/null @@ -1,15 +0,0 @@ -manifest: - schema: openstrata.plugin/v1alpha1 -plugin: - name: httpresolver - version: 0.9.0 - kind: usd-asset-resolver -license: Apache-2.0 -runtime: - openusd: ">=26.08,<27.0" -provides: - - usd-resolver:http -requires: - capabilities: [usd-stage-read] -usd: - plug_info: plugin/resources/httpresolver/plugInfo.json \ No newline at end of file diff --git a/plugins/pointcloud-copc/CMakeLists.txt b/plugins/pointcloud-copc/CMakeLists.txt index 9498401..47d47b5 100644 --- a/plugins/pointcloud-copc/CMakeLists.txt +++ b/plugins/pointcloud-copc/CMakeLists.txt @@ -49,11 +49,6 @@ if(NOT TARGET usdcopc::core) "${CMAKE_CURRENT_SOURCE_DIR}/../../libs/usd-copc" "${CMAKE_CURRENT_BINARY_DIR}/usd-copc") endif() -if(NOT TARGET HttpResolver) - add_subdirectory( - "${CMAKE_CURRENT_SOURCE_DIR}/../httpresolver" - "${CMAKE_CURRENT_BINARY_DIR}/httpresolver") -endif() if(NOT TARGET OpenGL::GL) find_package(OpenGL REQUIRED) endif() @@ -92,15 +87,20 @@ openstrata_link_openusd( COMPONENTS arch tf gf vt sdf pcp usd ar) if(USDGEO_BUILD_TESTS) + if(NOT TARGET HttpResolver) + add_subdirectory( + "${CMAKE_CURRENT_SOURCE_DIR}/../../tests/plugins/httpresolver" + "${CMAKE_CURRENT_BINARY_DIR}/httpresolver") + endif() add_executable(pointcloudCopc_tests src/ArAssetRandomAccessSource.cpp tests/test_pointcloud_copc.cpp) target_compile_features(pointcloudCopc_tests PRIVATE cxx_std_17) - target_link_libraries(pointcloudCopc_tests PRIVATE usdlaz::core + target_link_libraries(pointcloudCopc_tests PRIVATE usdcopc::core usdlaz::core usdgeo::cache usdpointcloud::authoring) target_compile_definitions(pointcloudCopc_tests PRIVATE USDGEOCOPC_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}" - USDGEOCOPC_HTTP_RESOLVER_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}/../httpresolver" + USDGEOCOPC_HTTP_RESOLVER_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}/../../tests/plugins/httpresolver" USDGEOLAS_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}/../pointcloud-las" USDGEOLAZ_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}/../pointcloud-laz") target_include_directories(pointcloudCopc_tests PRIVATE diff --git a/plugins/pointcloud-copc/README.md b/plugins/pointcloud-copc/README.md index bf0110f..d2db5f7 100644 --- a/plugins/pointcloud-copc/README.md +++ b/plugins/pointcloud-copc/README.md @@ -13,7 +13,7 @@ An external resolver such as `usd-http-resolver` is runtime composition through `PXR_PLUGINPATH_NAME`, not a build-time dependency of this bundle. The repository's resolver-backed integration test uses the independent -`plugins/httpresolver` bundle. That bundle is a test double which serves a +`tests/plugins/httpresolver` fixture. It is a Tier 1 test double which serves a local fixture as an in-memory asset; a production resolver can replace it without changing this plugin or `usdCopc`. See the [resolver-backed source contract](../../docs/architecture/RESOLVER_SOURCE.md). diff --git a/plugins/pointcloud-copc/tests/test_pointcloud_copc.cpp b/plugins/pointcloud-copc/tests/test_pointcloud_copc.cpp index 5d5aee0..fc687ac 100644 --- a/plugins/pointcloud-copc/tests/test_pointcloud_copc.cpp +++ b/plugins/pointcloud-copc/tests/test_pointcloud_copc.cpp @@ -3,6 +3,8 @@ #include "usdgeocopc/ArAssetRandomAccessSource.h" #include "usdgeocopc/UsdGeoCopcDiagnostics.h" #include "usdgeo/PointCloudCache.h" +#include "usdcopc/Copc.h" +#include "usdlas/Las.h" #include #include @@ -120,6 +122,40 @@ std::filesystem::path FindGeneratedCacheRoot( return {}; } +constexpr const char* CacheSentinelPath = "/CacheSentinel"; + +void WriteResolverCacheEntry(const usdgeo::cache::Layout& layout, + const pxr::SdfLayerHandle& sourceLayer, + bool corruptedRoot) { + std::error_code error; + std::filesystem::remove_all(layout.entryDirectory, error); + error.clear(); + std::filesystem::create_directories(layout.entryDirectory, error); + Check(!error, "create resolver cache entry"); + if (corruptedRoot) { + std::ofstream root(layout.rootLayer, + std::ios::binary | std::ios::trunc); + Check(root.good(), "create corrupted resolver cache root"); + root << "corrupted generated cache"; + Check(root.good(), "write corrupted resolver cache root"); + } else { + const auto cacheLayer = + pxr::SdfLayer::CreateAnonymous("resolver-cache-entry.usda"); + Check(sourceLayer && cacheLayer, "create resolver cache layer"); + cacheLayer->TransferContent(sourceLayer); + const auto sentinel = pxr::SdfPrimSpec::New( + cacheLayer->GetPseudoRoot(), "CacheSentinel", pxr::SdfSpecifierDef); + Check(static_cast(sentinel), "author resolver cache sentinel"); + Check(cacheLayer->Export(layout.rootLayer.string()), + "export resolver cache root"); + } + std::ofstream manifest(layout.manifest, + std::ios::binary | std::ios::trunc); + Check(manifest.good(), "create resolver cache manifest"); + manifest << "committed\n"; + Check(manifest.good(), "write resolver cache manifest"); +} + void TestArAssetRandomAccessSource() { auto asset = std::make_shared( std::vector{1, 2, 3, 4, 5}); @@ -653,6 +689,11 @@ void TestResolverBackedRead() { Check(resolverStability == usdgeo::cache::ResolverIdentityStability::Unavailable); SetHttpResolverIdentityMode(""); + Check(usdgeo::TryBuildResolverSourceIdentity( + pxr::ArGetResolver(), "http://memory.copc", + pxr::ArResolvedPath("http://memory.copc"), *resolvedAsset, + resolverIdentity, resolverStability, identityError), + "restore stable resolver identity"); char signature[4]{}; Check(resolvedAsset->Read(signature, sizeof(signature), 0) == @@ -678,43 +719,64 @@ void TestResolverBackedRead() { Check(firstPoints.GetPointsAttr().Get(&firstPositions)); Check(!firstPositions.empty()); + auto cacheSource = + std::make_shared( + resolvedAsset, "http://memory.copc"); + usdcopc::CopcReader cacheReader(cacheSource); + usdcopc::CopcHeader cacheHeader; + std::vector cacheDiagnostics; + Check(cacheReader.ReadMetadata(cacheHeader, cacheDiagnostics), + "read cache fixture metadata"); + usdpointcloud::PointChunk metadataChunk; + usdgeo::GeoReference cacheReference; + usdgeo::SpatialBounds cacheBounds; + std::string cacheErrorMessage; + Check(usdlas::BuildPointCloudMetadata( + cacheHeader.las, metadataChunk, cacheReference, cacheBounds, + cacheErrorMessage), "build cache fixture metadata"); + usdpointcloud::PointReadRequest cacheRequest; + Check(usdpointcloud::MakeReadRequest( + {}, cacheRequest, cacheDiagnostics, + usdpointcloud::PointReadFormat::Copc), "build cache read request"); + usdgeo::cache::Layout cacheLayout; + Check(usdgeo::TryBuildPointCloudCacheLayout( + cacheRoot, resolverIdentity, cacheReference, cacheRequest, + "copc-reader-1", cacheLayout, cacheErrorMessage), + "build resolver cache layout"); + WriteResolverCacheEntry(cacheLayout, layer, false); + const auto cachedLayer = pxr::SdfLayer::CreateAnonymous("resolver-cached.usda"); Check(cachedLayer); - usdgeo::cache::ResetLookupStatistics(); Check(format->Read(cachedLayer.operator->(), "http://memory.copc", false)); - const auto cacheHitStatistics = usdgeo::cache::GetLookupStatistics(); - Check(cacheHitStatistics.hits > 0, - "stable resolver identity did not reuse generated cache"); - - const auto generatedCacheRoot = FindGeneratedCacheRoot(cacheRoot); - Check(!generatedCacheRoot.empty(), - "resolver read did not publish a generated cache entry"); - std::filesystem::remove(generatedCacheRoot / "root.usdc"); - const auto recoveredLayer = - pxr::SdfLayer::CreateAnonymous("resolver-recovered.usda"); - Check(recoveredLayer); - usdgeo::cache::ResetLookupStatistics(); - Check(format->Read(recoveredLayer.operator->(), "http://memory.copc", - false)); - const auto recoveryStatistics = usdgeo::cache::GetLookupStatistics(); - Check(recoveryStatistics.incomplete > 0, - "corrupt resolver cache entry was not classified incomplete"); - - const auto regeneratedCacheRoot = FindGeneratedCacheRoot(cacheRoot); - Check(!regeneratedCacheRoot.empty(), - "incomplete resolver cache entry was not regenerated"); - std::ofstream corruptedRoot(regeneratedCacheRoot / "root.usdc", - std::ios::binary | std::ios::trunc); - Check(corruptedRoot.good()); - corruptedRoot << "corrupted generated cache"; - corruptedRoot.close(); - const auto corruptedLayer = - pxr::SdfLayer::CreateAnonymous("resolver-corrupted.usda"); - Check(corruptedLayer); - Check(format->Read(corruptedLayer.operator->(), "http://memory.copc", - false)); - Check(corruptedLayer->GetPrimAtPath(pxr::SdfPath("/PointCloud"))); + Check(cachedLayer->GetPrimAtPath(pxr::SdfPath(CacheSentinelPath)), + "stable resolver identity did not reuse generated cache"); + + const auto generatedCacheRoot = FindGeneratedCacheRoot(cacheRoot); + Check(generatedCacheRoot == cacheLayout.entryDirectory, + "resolver cache entry layout mismatch"); + std::filesystem::remove(cacheLayout.rootLayer); + const auto recoveredLayer = + pxr::SdfLayer::CreateAnonymous("resolver-recovered.usda"); + Check(recoveredLayer); + Check(format->Read(recoveredLayer.operator->(), "http://memory.copc", + false)); + Check(!recoveredLayer->GetPrimAtPath(pxr::SdfPath(CacheSentinelPath)), + "incomplete resolver cache entry was reused"); + Check(!std::filesystem::exists(cacheLayout.entryDirectory), + "incomplete resolver cache entry was not invalidated"); + + WriteResolverCacheEntry(cacheLayout, layer, true); + const auto corruptedLayer = + pxr::SdfLayer::CreateAnonymous("resolver-corrupted.usda"); + Check(corruptedLayer); + Check(format->Read(corruptedLayer.operator->(), "http://memory.copc", + false)); + Check(corruptedLayer->GetPrimAtPath(pxr::SdfPath("/PointCloud"))); + Check(!std::filesystem::exists(cacheLayout.entryDirectory), + "corrupted resolver cache entry was not invalidated"); + + WriteResolverCacheEntry(cacheLayout, layer, false); auto changedRecords = records; Write(changedRecords.front(), 0, std::int32_t{9000}); @@ -750,13 +812,9 @@ void TestResolverBackedRead() { const auto changedLayer = pxr::SdfLayer::CreateAnonymous("resolver-changed.usda"); Check(changedLayer); - usdgeo::cache::ResetLookupStatistics(); Check(format->Read(changedLayer.operator->(), "http://memory.copc", false)); - const auto changedStatistics = usdgeo::cache::GetLookupStatistics(); - Check(changedStatistics.hits == 0, - "changed resolver validation token reused the old cache entry"); - Check(changedStatistics.misses > 0, - "changed resolver validation token did not produce a cache miss"); + Check(!changedLayer->GetPrimAtPath(pxr::SdfPath(CacheSentinelPath)), + "changed resolver validation token reused the old cache entry"); const auto changedStage = pxr::UsdStage::Open(changedLayer); Check(changedStage); const auto changedPoints = pxr::UsdGeomPoints::Get( diff --git a/plugins/httpresolver/CMakeLists.txt b/tests/plugins/httpresolver/CMakeLists.txt similarity index 79% rename from plugins/httpresolver/CMakeLists.txt rename to tests/plugins/httpresolver/CMakeLists.txt index 9e6d7f8..e987427 100644 --- a/plugins/httpresolver/CMakeLists.txt +++ b/tests/plugins/httpresolver/CMakeLists.txt @@ -11,7 +11,7 @@ endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -include("${CMAKE_CURRENT_SOURCE_DIR}/../pointcloud-las/cmake/OpenStrataPlugin.cmake") +include("${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/pointcloud-las/cmake/OpenStrataPlugin.cmake") openstrata_default_build_type() if(NOT TARGET OpenGL::GL) @@ -51,10 +51,3 @@ openstrata_configure_plugin( PLUG_INFO_OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/plugin/resources/httpresolver/plugInfo.json") -openstrata_install_plugin_bundle( - TARGET ${PLUGIN_NAME} - RESOURCES - "${CMAKE_CURRENT_SOURCE_DIR}/plugin/resources/httpresolver/plugInfo.json" - RESOURCE_DESTINATION "plugin/resources/httpresolver" - MANIFESTS - "${CMAKE_CURRENT_SOURCE_DIR}/openstrata.plugin.yaml") \ No newline at end of file diff --git a/plugins/httpresolver/README.md b/tests/plugins/httpresolver/README.md similarity index 51% rename from plugins/httpresolver/README.md rename to tests/plugins/httpresolver/README.md index 1010ed7..b080879 100644 --- a/plugins/httpresolver/README.md +++ b/tests/plugins/httpresolver/README.md @@ -13,11 +13,16 @@ without changing this plugin or `usdCopc`. ## Status -`v0.10.0` resolves this bundle's disposition: it is removed once equivalent -external integration coverage exists, or relocated to an explicitly test-only -path such as `tests/plugins/httpresolver/`. Until then it must not be presented -as equivalent to a production point-cloud bundle. +The fixture lives under `tests/plugins/httpresolver/` so that Tier 1 resolver +contract tests remain reproducible without an external repository. It is built +only as a dependency of the COPC integration tests and has no OpenStrata bundle +manifest or install rule, so it is excluded from product discovery, packaging, +the plugin matrix, and release metadata. + +Tier 2 interoperability uses +[`usd-http-resolver`](https://github.com/animu-sphere/usd-http-resolver) as +runtime composition and does not link this repository to it. See the -[resolver-backed source contract](../../docs/architecture/RESOLVER_SOURCE.md) -and the [workspace contract](../../docs/architecture/WORKSPACE.md). +[resolver-backed source contract](../../../docs/architecture/RESOLVER_SOURCE.md) +and the [workspace contract](../../../docs/architecture/WORKSPACE.md). diff --git a/plugins/httpresolver/plugin/resources/httpresolver/plugInfo.json b/tests/plugins/httpresolver/plugin/resources/httpresolver/plugInfo.json similarity index 100% rename from plugins/httpresolver/plugin/resources/httpresolver/plugInfo.json rename to tests/plugins/httpresolver/plugin/resources/httpresolver/plugInfo.json diff --git a/plugins/httpresolver/plugin/resources/httpresolver/plugInfo.json.in b/tests/plugins/httpresolver/plugin/resources/httpresolver/plugInfo.json.in similarity index 100% rename from plugins/httpresolver/plugin/resources/httpresolver/plugInfo.json.in rename to tests/plugins/httpresolver/plugin/resources/httpresolver/plugInfo.json.in diff --git a/plugins/httpresolver/src/HttpResolver.cpp b/tests/plugins/httpresolver/src/HttpResolver.cpp similarity index 100% rename from plugins/httpresolver/src/HttpResolver.cpp rename to tests/plugins/httpresolver/src/HttpResolver.cpp diff --git a/tools/check_release_metadata.py b/tools/check_release_metadata.py index 9f3b9dd..50e75bc 100644 --- a/tools/check_release_metadata.py +++ b/tools/check_release_metadata.py @@ -30,10 +30,6 @@ def main() -> int: rf'^ version: {re.escape(version)}$', "plugins/pointcloud-ply/CMakeLists.txt": rf'^ VERSION {re.escape(version)}$', - "plugins/httpresolver/openstrata.plugin.yaml": - rf'^ version: {re.escape(version)}$', - "plugins/httpresolver/CMakeLists.txt": - rf'^ VERSION {re.escape(version)}$', } failures = [] @@ -54,4 +50,4 @@ def main() -> int: if __name__ == "__main__": - raise SystemExit(main()) \ No newline at end of file + raise SystemExit(main()) From b26635378c8a5a77eeca65e7c6f8e05d4a0ac9bf Mon Sep 17 00:00:00 2001 From: snkmcb Date: Sun, 23 Aug 2026 00:08:25 +0900 Subject: [PATCH 2/2] docs(resolver): correct Tier 1 CI coverage claims to match reality The relocation commit documented Tier 1 resolver contract tests as the "required CI gate" and described the test double as built transitively by the COPC integration test in CI. Neither is true. Every cell in openstrata.ci.yaml is a per-plugin bundle build rooted at plugins/pointcloud-/CMakeLists.txt, which is a standalone project() that never declares the USDGEO_BUILD_TESTS option. The option is undefined there, so the guarded block never runs and neither pointcloudCopc_tests nor the resolver fixture is compiled in CI. USDGEO_BUILD_TESTS defaults to ON only in the root CMakeLists.txt, which is what the local `ost build` gate configures. Restate Tier 1 as the required local gate across WORKSPACE.md, RESOLVER_SOURCE.md, infrastructure-maturity.md, and CHANGELOG.md, and record that wiring it into the CI matrix is outstanding follow-up work. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 8 ++++++-- docs/architecture/RESOLVER_SOURCE.md | 4 ++-- docs/architecture/WORKSPACE.md | 17 +++++++++++------ docs/roadmap/infrastructure-maturity.md | 7 +++++-- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72feb1e..09d34c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,8 +39,12 @@ diagnostic completion and recorded external interoperability remain planned. ### Changed - Removed standalone `httpresolver` product CI cells. The relocated test double - is built transitively by the COPC Tier 1 integration test, keeping the - required gate independent of external resolver repositories. + is built transitively by the COPC Tier 1 integration test in the root build, + keeping the local gate independent of external resolver repositories. Note + that Tier 1 is not yet part of the CI matrix: every declared cell builds a + single plugin bundle, where `USDGEO_BUILD_TESTS` is undefined, so neither the + fixture nor `pointcloudCopc_tests` is compiled there. Wiring Tier 1 into CI + is tracked as follow-up work. - Added a shared cache-layout construction entry point so producer and consumer tests derive resolver-backed cache entries from the same descriptor contract. diff --git a/docs/architecture/RESOLVER_SOURCE.md b/docs/architecture/RESOLVER_SOURCE.md index e38f35d..2d35283 100644 --- a/docs/architecture/RESOLVER_SOURCE.md +++ b/docs/architecture/RESOLVER_SOURCE.md @@ -245,7 +245,7 @@ dependency. Registration is in [INSTALL.md](../guides/INSTALL.md). **Tier 1 — repository-local contract tests.** They run with no external resolver repository, using fake or memory-backed test assets, and remain the -required CI gate. Coverage: resolver-backed random access, partial reads, +required local gate; CI wiring is outstanding. Coverage: resolver-backed random access, partial reads, short-read diagnostics, stable / unstable / unavailable identity, miss-to-hit behavior, invalidation on validation-token change, corruption recovery, `TilePlan` compatibility in cache keys, and deterministic diagnostics. @@ -263,7 +263,7 @@ dependent on the resolver repository. designated first Tier 2 implementation. Its `v0.2.0` release provides the HTTP backend and OpenUSD resolver bundle, exposes stable resolver-neutral identity through `ArAssetInfo`, and is tested through its own OpenStrata workflow. Tier -1 remains this repository's required CI gate; Tier 2 is now ready to be +1 remains this repository's required local gate; Tier 2 is now ready to be composed and recorded as the `v0.10.0` release gate. ## 7. Test-double resolver diff --git a/docs/architecture/WORKSPACE.md b/docs/architecture/WORKSPACE.md index 91ccfc8..a46caa4 100644 --- a/docs/architecture/WORKSPACE.md +++ b/docs/architecture/WORKSPACE.md @@ -267,8 +267,10 @@ Every structural or format change preserves these invariants: `openstrata.ci.yaml` is the source of truth; the GitHub workflow is generated by `ost ci generate github`. The declared PR matrix runs every production -bundle on every host. The Tier 1 resolver test double is built transitively by -the COPC integration test and has no product-bundle cell: +bundle on every host. Every cell is a per-plugin bundle build, so the Tier 1 +resolver test double has no product-bundle cell and is not built by CI; it is +built by the root `ost build` in the local gate below, where +`USDGEO_BUILD_TESTS` defaults to `ON`: | Host | Target | OST level | | --- | --- | --- | @@ -292,14 +294,17 @@ ost plugin test plugins/pointcloud-copc --up-to 4 The LAS, LAZ, COPC, and PLY bundles declare OST smoke fixtures and run the L3 `usdcat.read` and L4 `python.stage_open` checks. The test-only `httpresolver` -bundle has no standalone fixture or CI matrix cell because its functional path -is exercised by the COPC Tier 1 integration test. The COPC bundle follows the +bundle has no standalone fixture or CI matrix cell; its functional path is +exercised by the COPC Tier 1 integration test in the root build, which the +local gate runs and CI does not. The COPC bundle follows the same runtime matrix as LAS and LAZ. The gate must stay passable without any external resolver repository. From `v0.10.0`, repository-local resolver contract tests (Tier 1) are the required -CI gate, and cross-repository integration against an external resolver -implementation (Tier 2) is composed separately; see +*local* gate; wiring them into the CI matrix is still outstanding, since the +declared cells build plugin bundles individually rather than the repository +root. Cross-repository integration against an external resolver implementation +(Tier 2) is composed separately; see [RESOLVER_SOURCE.md](RESOLVER_SOURCE.md). ## 10. Delivery status diff --git a/docs/roadmap/infrastructure-maturity.md b/docs/roadmap/infrastructure-maturity.md index 9ad4957..eb7d553 100644 --- a/docs/roadmap/infrastructure-maturity.md +++ b/docs/roadmap/infrastructure-maturity.md @@ -438,7 +438,9 @@ and is excluded from the product plugin and release matrices. #### Tests Tier 1 runs without any external resolver repository, using fake or -memory-backed test assets, and remains the required CI gate: resolver-backed +memory-backed test assets, and remains the required local gate; it is not yet +wired into the CI matrix, whose cells build plugin bundles individually rather +than the repository root: resolver-backed random access, partial reads, short-read diagnostics, the three identity states, miss-to-hit behavior, invalidation on token change, corruption recovery, `TilePlan` compatibility inputs, and deterministic diagnostics. @@ -596,7 +598,8 @@ Architecture-level tests accompany correctness tests: From `v0.10.0`, resolver coverage is split in two tiers. Tier 1 runs repository-local contract tests against fake or memory-backed resolver assets -and is the required CI gate; Tier 2 composes an external resolver +and is the required local gate, not yet a CI cell; Tier 2 composes an external +resolver implementation and a local reproducible server for end-to-end verification without making this repository structurally depend on it.