Skip to content

feat: report what a runtime can read through formats() - #4

Merged
snkmcb merged 2 commits into
mainfrom
feat/sdk-formats
Sep 5, 2026
Merged

snkmcb merged 2 commits into
mainfrom
feat/sdk-formats

Conversation

@snkmcb

@snkmcb snkmcb commented Sep 5, 2026

Copy link
Copy Markdown
Member

Closes the formats half of P2 in docs/roadmap/current.md. inspect is untouched: it still has no defined result, so it stays intent.

The contract this settles

docs/design/sdk.md left open whether formats() reports the capabilities the composition resolved, the extensions OpenUSD registered, or their intersection. It reports none of the three. The two lists differ exactly when a plugin fails to load — which is when the answer matters most — so each extension carries the state that says which source claimed it:

State Meaning
available composed and registered; open will dispatch on it
not_loaded composed, not registered: the plugin shipped and did not load, and component names what to look at
undeclared registered, never composed: OpenUSD's own usda, usdc, usdz

Lane split

The same rule runtime_info follows — describing a runtime must not require loading it. Only the registered list needs a loaded OpenUSD, so formats() sits in the USD lane while the join, format_support(), sits in the core lane and is tested per commit against a fixture lock and a fixed registered list. No signature in the USD-lane header exposes a pxr:: type, so a future binding can project formats without projecting OpenUSD.

schemas/formats.v1.json is held from both sides the way runtime-info is: test_core.cpp asserts the SDK produces sdk/core/tests/data/formats.json byte for byte, and tests/tooling validates that same document against the schema and against the lock it was read from. The fixture lock gains a second file-format plugin so the committed document carries all three states.

Also here

  • open() builds its capability detail through the core lane, so the capability a UGEO-E031 names and the one formats() reports cannot drift.
  • The two failure paths the tests could not reach are now covered. runtime_info() refuses a lock that is not a regular file instead of leaving it to the stream (opening a directory fails on some platforms and reads as empty on others; the caller must get UGEO-E012 either way), and fixtures/malformed.usda supplies the asset of a composed format that OpenUSD refuses, which is the only route to UGEO-E032.

Test plan

Run locally, all passing:

  • cmake -S sdk -B build/sdk-core -DUSDGEOSPATIAL_BUILD_USD=OFF + build + ctest — clean build, no warnings
  • python tests/tooling/test_metadata_tools.py — 17 tests, including the two new formats contracts
  • python tools/validate_metadata.py — all checks passed

Not run: the OpenUSD lane. There is no composed prefix on this machine and no CI runner for that lane, so sdk/usd/src/formats.cpp, the new test_open.cpp cases, and the tests/native-consumer addition are uncompiled. They depend on SdfFileFormat::FindAllFileFormatExtensions(). Anyone with a composed prefix can check them with the commands in sdk/README.md.

🤖 Generated with Claude Code

snkmcb and others added 2 commits September 5, 2026 21:52
docs/design/sdk.md left the contract of formats() open: whether it reports the
capabilities the composition resolved, the extensions OpenUSD registered, or
their intersection. It reports none of the three. The two lists differ exactly
when a plugin fails to load, which is when the answer matters most, so every
extension carries the state that says which source claimed it: available for
both, not_loaded for a composed plugin OpenUSD did not register, and undeclared
for a registered format no capability declared, which is how OpenUSD's own
usda, usdc, and usdz appear. Collapsing the lists would report a broken
composition as a healthy one, or hide the failure entirely.

The split between the lanes follows the rule runtime_info already follows:
describing a runtime must not require loading it. Only the registered list
needs a loaded OpenUSD, so formats() is in the USD lane while format_support(),
the join, is in the core lane and is tested per commit against a fixture lock
and a fixed registered list. Its serialized form is schemas/formats.v1.json,
held from both sides the way runtime-info is: the SDK is asserted to produce
sdk/core/tests/data/formats.json byte for byte, and the tooling tests validate
that same document against the schema and against the lock it was read from.
The fixture lock gains a second file-format plugin so the committed document
carries all three states.

open() now builds the capability name it reports through the core lane, so the
capability a UGEO-E031 names and the one formats() reports cannot drift.

The two failure paths the tests could not reach are covered. runtime_info()
refuses a lock that is not a regular file rather than leaving it to the stream,
because opening a directory fails on some platforms and succeeds and reads as
empty on others, and the caller must get UGEO-E012 either way. UGEO-E032 needs
an asset of a composed format that OpenUSD refuses, which fixtures/malformed.usda
now supplies.

The OpenUSD lane still has no CI runner, so formats() and the new open()
coverage remain unproven until it exists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The state that justified reporting both lists could not be reached for the
failure it names. SdfFileFormat::FindAllFileFormatExtensions builds its index
from plugInfo.json metadata and never loads a plugin, while open() dispatches
through FindByExtension, which does; a plugin that ships and whose library
fails to load was therefore reported available by formats() and
capability_unavailable by open() in the same process, and not_loaded was
unreachable. registered_extensions now asks OpenUSD for the file format of
each candidate extension, which is the same call open() makes, so the report
agrees with the operation it predicts. Answering costs the load of every
registered format plugin, and that cost is documented where the operation is.

The capability of a composed format is now the canonical spelling of its
extension rather than the lock's literal string. The two are the same in every
conforming lock; where they differ, an entry that kept the raw name would name
a capability its own extension contradicts and would fail
schemas/formats.v1.json. The USD-lane test counts declared formats as
extensions rather than capabilities for the same reason.

The unreadable-lock test built its fixture at a fixed path under the temp
directory, so two runs of the suite at once -- a multi-config build, ctest -j,
two jobs on one runner -- would delete each other's fixture and fail on
runtime_not_found instead. The path is now unique per process.

Two comments were wrong rather than the code. runtime_info's new guard listed a
dangling symlink as a case it catches, but exists() follows links, so that is
already runtime_not_found. malformed.usda was called an asset of a composed
format, when usda is the canonical undeclared format under the vocabulary this
same change introduces: OpenUSD provides it, no capability declares it. The
roadmap's "so nothing about them is proven" reversed the sense of a contrast.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@snkmcb
snkmcb merged commit 7c06143 into main Sep 5, 2026
2 checks passed
@snkmcb
snkmcb deleted the feat/sdk-formats branch September 5, 2026 13:15
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