Repository navigation
feat: establish the C++ SDK with typed failures and runtime introspection - #3
Merged
Merged
Conversation
…tion Implement the first slice of the SDK that docs/design/sdk.md describes and docs/roadmap/current.md schedules as P2: Result<T>, the published diagnostic codes, the runtime_info schema, and open. The SDK builds in two lanes. The core lane compiles with no OpenUSD present and carries the diagnostics, the result type, and runtime_info; the OpenUSD lane carries open. The split is not cosmetic: describing a runtime must not require loading it, so runtime_info reads the materialized prefix's own metadata/composition.lock.json and can report a missing or wrong runtime before any OpenUSD library loads. It is also what lets CI check these contracts on a runner with no composed runtime. open returns an ordinary pxr::UsdStageRefPtr. What it adds is a typed failure in place of the null pointer and loose TF_ERROR text OpenUSD returns, naming each condition separately: an unhandled URI scheme, a format the composition does not install (reported as the capability a composition would have to add, such as usd-fileformat:geojson), an unresolved asset, and a stage OpenUSD declined to compose. The runtime-info contract is held from both sides. sdk/core/tests/test_core.cpp asserts the SDK produces exactly the committed document in sdk/core/tests/data/runtime-info.json, and tests/tooling asserts that the same document satisfies schemas/runtime-info.v1.json, agrees with the lock it was read from, and lists the same codes sdk/core/src/diagnostics.cpp defines. Neither side can move alone. Add tools/sdk_env.py, which prepends a composition's search paths instead of replacing the environment. The composed OpenUSD links the host CPython and this composition deliberately bundles no interpreter, so a native executable started under `ost runtime exec` cannot load. That runner stays the one for anything whose result is a claim about the runtime. tests/native-consumer now also builds against the installed public headers through the exported CMake package, so an incomplete install set fails there rather than in review. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Six findings from the review of the SDK, in the order they matter: to_json promised a document conforming to schemas/runtime-info.v1.json while validating only one of the fields the schema constrains. A lock missing a provider digest serialized as `"artifact": ""` and failed the schema at whoever consumed the output rather than where the cause was known. from_lock_json now checks all three identities, every component kind, version, and artifact, and every capability version and artifact, and decompose_target requires a toolchain that starts with a letter as the schema spells it. open took its TfErrorMark after the calls that trigger resolver and plugin registration, so errors from a plugin that failed to load -- the case that explains a missing capability -- were neither reported nor cleared, and spilled onto stderr or into a caller's own mark. The mark is now taken before the first call into OpenUSD and cleared on every path, and the three conditions the SDK decides for itself carry that text as an `openusd` detail. The JSON reader converted numbers with std::strtod, which follows LC_NUMERIC: a host that had called setlocale on a comma-decimal machine would have rejected any fractional number, and with it the whole lock. Numbers are now scanned by the JSON grammar, which also rejects malformed values such as `1-2e+3` that the old scan accepted, and converted through the classic locale. runtime_info materialized the entire 6.2 MB lock to read twenty fields, on the path documented as the cheap probe a caller runs before OpenUSD loads. The parser takes an optional member filter, and runtime_info keeps only the three top-level members it reads plus the destination of each install entry. Parsing the committed lock drops from 24.4 ms to 6.1 ms and from roughly a hundred thousand nodes to about four thousand. tools/sdk_env.py --print omitted the settings variables that the runner applies, so it described an environment the runner does not build. docs/roadmap/current.md listed running the SDK lanes in CI as outstanding while the same change added the core lane job; only the OpenUSD lane remains. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the first slice of P2 in current priorities:
Result<T>, the published diagnostic codes, theruntime_infoschema, andopen.P0 (compose the vector capability) is blocked outside this repository —
ghcr.io/animu-sphere/usd-vector-pluginsis not published, so there is no OCI digest to pin — and P1 is sequenced after it, so this takes P2 instead.Two lanes
usdgeospatial::coreResult<T>,Diagnostic,runtime_infousdgeospatial::sdkopenThe split is not cosmetic. Describing a runtime must not require loading it: an application that finds no runtime, or the wrong one, has to be able to say so.
runtime_infotherefore reads the materialized prefix's ownmetadata/composition.lock.jsonrather than asking OpenUSD, which also lets CI check the diagnostics, result, and introspection contracts on a runner with no composed runtime.What
openaddsIt returns an ordinary
pxr::UsdStageRefPtr— OpenUSD stays reachable below it. What it replaces is the null pointer plus looseTF_ERRORtext, by naming each condition separately:{ "code": "UGEO-E031", "name": "capability_unavailable", "category": "asset", "subsystem": "sdk", "message": "this composition installs no file format for the extension", "details": { "uri": "boundaries.geojson", "extension": "geojson", "capability": "usd-fileformat:geojson" } }The
capabilitydetail uses the same name the manifest and the runtime metadata use, so a consumer can report exactly which capability a composition would have to add. GeoJSON is the honest example: it is P0, so this composition genuinely cannot open one, and the SDK's own test asserts that.Codes are published in docs/reference/diagnostics.md. There is deliberately no generic
unknown.The runtime-info contract is held from both sides
sdk/core/tests/test_core.cppasserts the SDK produces exactly the committedsdk/core/tests/data/runtime-info.json.tests/toolingasserts that same document satisfiesschemas/runtime-info.v1.json, agrees with the lock it was read from (including the target decomposition, viaruntime_metadata.target_identity), and lists the same codessdk/core/src/diagnostics.cppdefines.Neither side can move alone. Drift detection was checked by mutation: a changed toolchain, host Python version, bundled flag, USD version, and a renamed or renumbered code all fail.
tools/sdk_env.pyost runtime execreplaces the environment, which is what makes an acceptance result a claim about the composition alone. A native executable cannot use it: the composed OpenUSD links the host CPython (usd_pythonagainstpython313.dll) and this composition deliberately bundles no interpreter, so such a process fails to load before it runs a line.tools/sdk_env.pyprepends the same paths instead.ost runtime execstays the runner for anything whose result is a claim about the runtime.Verification
Run clean, from a fresh compose of the committed lock:
fixtures/three-points.copctests/native-consumeragainst the installed headers and exported CMake package: ctest 2/2python tools/validate_metadata.py: all checks pass;tests/tooling: 15/15CI gains a
sdk-corejob. The OpenUSD lane needs a composed prefix and a matching toolchain, so it belongs with acceptance rather than with the per-commit checks.Known limits
inspectandformatsare not implemented.formatsfirst needs a decision on whether it reports composed capabilities, OpenUSD's registered extensions, or the intersection — those differ exactly when a plugin fails to load, which is when the answer matters most.msvc143its target names; nomsvc143toolset was available on the development machine.sdk/README.mdstates that building with the target's toolchain is the supported configuration.stage_open_failedandruntime_metadata_unreadablehave no test that reaches them through a real malformed asset or an unreadable file. Both are recorded in the roadmap.🤖 Generated with Claude Code