Skip to content

feat(resolver): close v0.10.0 resolver-backed source identity - #134

Merged
snkmcb merged 9 commits into
mainfrom
feat/v0.10.0-resolver-diagnostics
Aug 23, 2026
Merged

feat(resolver): close v0.10.0 resolver-backed source identity#134
snkmcb merged 9 commits into
mainfrom
feat/v0.10.0-resolver-diagnostics

Conversation

@snkmcb

@snkmcb snkmcb commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator

Closes the v0.10.0 milestone: the two phases that were still open — diagnostics and secrets, and validation and baselines — plus the review fixes that followed.

What was missing

Phase 5 and Phase 6 of the resolver-backed source contract were unchecked. One diagnostic category out of seven was emitted, Tier 1 was a local gate wearing a CI gate's description, and Tier 2 had never been run.

Diagnostics

usdgeo::cache::CacheDecision publishes the seven stable, transport-neutral categories the contract names. Messages are fixed constants owned by usdgeo::cache, so a transport detail cannot reach one by accident. COPC projects them onto four codes, COPC009COPC012, and every message names its exact category.

resolver-identity-changed was not observable at all. A changed validation token produced a different single-level cache key, so a superseded revision was indistinguishable from a source never generated before. The entry layout is now two levels:

<cache root>/<generation key>/<source identity key>/

Caller intent chooses the directory; everything read out of the source chooses the entry inside it. That rule is load-bearing: the georeference is source-derived (its local origin is the source bounding box), and so is the conversion tool's tile-plan key, so both live in the identity half. A unit test holds both directions and was written against the defect first, observed to fail, then fixed.

Both path components are hashes, which is also how a signed URL stays unreadable from a cache root.

Tier 1 as a CI gate

Every declared cell was a per-plugin bundle build rooted at a standalone project() that never declares USDGEO_BUILD_TESTS, so pointcloudCopc_tests and the resolver test double were compiled only by the local root build. OpenStrata 0.22.2's kind: workspace cells configure the repository root; six of them now run on every host and both lanes, with no external resolver repository involved.

Tier 2, recorded

Composed at runtime with usd-http-resolver v0.4.0 over the 81 MB Autzen COPC. Neither repository is in the other's build graph.

Scenario Validator Identity Codes Requests Bytes fetched Selectivity
metadata only strong stable 3 120,546 0.001486
full read strong stable COPC010 277 81,123,042 1.000000
full read weak unstable COPC009 277 81,123,042 1.000000

Local, remote, and second-revision reads author the same 10,653,336 points under the same SHA-256. Three revisions are served at one identifier and differ only in the validator, so equal identifiers not implying equal content is demonstrated rather than asserted. The weak-validator row is the conservative fallback proven against a real resolver rather than a test double — and it still authors identical output, because a disabled cache changes what is reused, never what is read.

Two corrections, recorded rather than papered over

  • RESOLVER_SOURCE.md §3.2 described the converter as accepting resolver-addressable identifiers. It accepts .las/.laz local paths, so nothing publishes a generated entry for a COPC source and a generated-cache hit ratio is not measurable end to end. Marked not implemented, and the reason a hit ratio is absent from the baseline.
  • WORKSPACE.md listed COPC among the bundles declaring an OST smoke fixture. It declares none, so its L3/L4 checks skip.

Both are pre-existing gaps outside this milestone's scope, now open work in implementation-status.md.

Breaking change

A v0.9.0 cache root is never looked up under the new layout, so the first run after upgrading regenerates. Entries are derived data. Tooling that enumerated entries with a single-level glob needs a second level — see MIGRATION.md.

Verification

ost build, ost test (16/16), ost plugin test for all four bundles, check_release_metadata.py, ost lock --check, and ost ci validate all pass locally on Windows cy2026/usd.

The v0.10.0 tag is not yet created; it belongs on the merge commit.

🤖 Generated with Claude Code

snkmcb and others added 9 commits August 23, 2026 00:23
v0.10.0 phase 5 required stable diagnostics for the seven resolver and cache
categories the resolver-backed source contract publishes. Only one of them was
emitted, and one of them - identity changed - was not observable at all: a
changed validation token produced a different single-level cache key, so a
superseded revision was indistinguishable from a source never generated.

Split the cache layout into a generation directory chosen by what would be
generated and an entry chosen by which revision was read, so revisions of the
same source collect side by side. Source size and modification time move to the
identity half with the validation token, since a source that changes size must
still land beside the entry it supersedes. Both path components stay hashes, so
no resolved identifier or validation token reaches the filesystem.

Add usdgeo::cache::CacheDecision with stable names and fixed, transport-neutral
messages, report decisions out of the authoring cache bridge, and project them
onto four COPC codes that each carry their exact category. Cover the vocabulary,
the layout split, superseded-entry detection, and the rule that no manifest
persists source identity material.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every declared cell was a per-plugin bundle build rooted at a standalone
project() that never declares USDGEO_BUILD_TESTS, so pointcloudCopc_tests and
the memory-backed resolver test double were compiled only by the local root
build. Tier 1 was therefore a local gate wearing a CI gate's description.

OpenStrata 0.22.2 has `kind: workspace` cells, which configure the repository
root - where USDGEO_BUILD_TESTS defaults to ON - and run its CTest suite. Add
one per host on both lanes and regenerate the workflow. The cells need no
external resolver repository, which is the property the tier split exists to
protect.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The v0.10.0 exit gate requires Tier 2 recorded against a released external
resolver before the tag, and remote baselines including bytes fetched over
source size. Both were outstanding.

Add a loopback origin that honours Range and logs every request, and a harness
that composes it with usd-http-resolver v0.4.0 and the COPC FileFormat. Each
scenario runs in a fresh interpreter against a fresh origin, so a row's byte
count is that row's cost.

Recorded against the 81 MB Autzen COPC: a metadata open costs 3 requests and
0.15% of the asset, a full read costs 277 requests and exactly 1.0, and local,
remote, and second-revision reads author the same 10,653,336 points under the
same digest. A weak validator classifies as unstable and reports COPC009 while
authoring identical output, which is the conservative fallback demonstrated
against a real resolver rather than a test double.

The report also states what the baseline does not measure: no COPC read has a
generated entry to hit, because the converter accepts .las and .laz only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Close the resolver-backed source identity milestone in the documents that
carry it, and bump every version declaration.

Two claims are corrected rather than restated. RESOLVER_SOURCE.md described
usd-pointcloud-convert as accepting resolver-addressable identifiers; it accepts
.las and .laz local paths, so nothing publishes a generated entry a COPC read
could hit, and that is now marked not implemented and recorded as open work
alongside the release notes' known limitations. WORKSPACE.md listed COPC among
the bundles declaring an OST smoke fixture; it declares none, so its L3 and L4
checks skip.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review found the two-level cache layout could not do the job it was added for.
The generation key included the resolved georeference, whose local origin is the
source bounding box, and the conversion tool put its tile-plan key - computed by
scanning the source - in the same half. A revision that moved either therefore
landed in an unrelated generation directory, where HasSupersededIdentityEntry
could not see it superseded anything: COPC011 would never fire, and the old
entry was orphaned beyond Invalidate's reach. That is exactly the case the split
exists to make observable.

Restate the split as one rule. Caller intent chooses the generation directory;
everything read out of the source chooses the entry inside it. coordinateTransform
moves to the identity half, Descriptor gains a sourceDerived group, and the
converter's tile-plan key moves into it. The caller's explicit coordinate
arguments are a different value and stay in the generation key with the rest of
the normalized arguments, so two differing requests are still not siblings.

A unit test now holds both directions and fails if either is broken; it was
written against the defect first and observed to fail.

Also from review:

- The Tier 2 harness bound port 0 per scenario, so "three revisions of one
  identifier" were three URLs and the published baseline claim was false. All
  origins now share one reserved port and one cache root, and the harness fails
  if the identifier turns out not to be single. Re-recorded.
- The baseline said what the record could not show. It now states that COPC011
  and a cache hit are unreachable from this harness because nothing publishes a
  COPC entry.
- The COPC adjacency assertion rebuilt the layout from the original georeference,
  so it held by construction. It recomputes from the changed header the way a
  read does; the fixture's header bounds are fixed, so the moved-georeference
  case is covered at the cache layer instead.
- The decision-message test asserted a string it had just built. The projection
  moved into the diagnostics header, and the test asserts code, text, category
  suffix, and severity as the plugin emits them.
- CacheDecision::ReuseDisabled was unreachable while three documents listed it
  under COPC009. A remote source with a relative payload directory now reports
  it, which is a real refusal that was previously silent.
- Origin.__init__ could raise after Popen and orphan the server, holding the
  reserved port for the rest of the run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@snkmcb
snkmcb merged commit 5695332 into main Aug 23, 2026
17 checks passed
@snkmcb
snkmcb deleted the feat/v0.10.0-resolver-diagnostics branch August 23, 2026 05:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant