Skip to content

usd_import: read USD through Moho (scene-index S2) - #415

Merged
doubleailes merged 95 commits into
mainfrom
openspec/moho-import
Oct 27, 2026
Merged

doubleailes merged 95 commits into
mainfrom
openspec/moho-import

Conversation

@doubleailes

Copy link
Copy Markdown
Owner

Summary

Step S2 of Mantle's scene-index program. Crust's USD import now reads the stage through Moho, a Hydra 2.0-style scene index over openusd. The direct usd::Prim importer is removed: there is one route and no switch. An IPR frame and crust render -i read a scene the same way, and later live updates can work from Moho's dirty locators.

  • Reading through Moho. Every schema family reads Hydra prims, schemas and generic attribute data from a Moho index:

    • settings and products;
    • camera;
    • transforms and motion;
    • meshes, implicit shapes and curves;
    • materials;
    • lights and light links;
    • volumes;
    • native and point instancing.

    A small stage uses one standard chain. A streamed stage uses masked chunk inputs merged with a payload-free scene input.

  • Gated on parity. Each family moved on its own, gated by baselines recorded from the direct importer before anything moved:

    • for every sample and fixture: a scene digest, the crust check --json report and a 16 spp image hash;
    • for ALab and Moana: the report, the EXR, memory and time.

    Behaviour changes and accepted exceptions are listed in the design record (openspec/specs/usd-scene-import/design.md). They include:

    • undefined prims following USD's rule;
    • material bindings following Hydra's model;
    • inexact shutter offsets (f32, as Hydra);
    • cross-chunk and cross-instancer prototype reuse (D3c), which changes unique-triangle counts only, with images equal.
  • openusd comes only through Moho (moho::openusd, moho::openusd_schemas), so Moho's pin is the stack's openusd rev. Moho is pinned at d09cc946.

  • Removed: the epoch and EvalTimeScope. Stage listing helpers moved to listing.rs, and authored transform ops to stage_facts.

  • hdCrust on hosts with glibc older than 2.38. hydra/hdCrust/test/crust_sysroot.sh and the pixi tasks compare-sysroot and interaction-sysroot run the CLI under the pixi sysroot's glibc. Then both sides of the comparison use the same libm. The underlying package issue is openusd 26.08 (linux-64) needs glibc 2.38 but its metadata allows any glibc anderslanglands/aswf-pixi#57.

Capability

usd-scene-import. The change is openspec/changes/moho-import/ (53/53 tasks, validates with --strict). Also updated:

  • docs/architecture.md: Moho in the crate map and render flow, the index/ module, and two invariants (an index reader changes together with its in-place rebuild; one openusd, through Moho);
  • the site page docs/architecture/usd-import.md.

Image impact

  • Bit-identical: scripts/check_images.sh reports all 48 scenes identical.
    • The cornellbox_guided golden was an outlier of the nondeterministic guided render. It was re-rendered with the direct importer: 4 direct and 6 index renders agree.
    • ALab and Moana: report and EXR equal their baselines. Those baselines were re-recorded for the accepted D3c prototype reuse, and only unique-triangle counts changed.
  • Changed intentionally
  • Not applicable

Performance

Reported, not gated. These are not bench_ab.sh A/B runs: they come from the parity runs, each alone on the machine.

Scene Peak memory Parse USD stage
ALab 21.14 GiB (1.93x) 57.1 s (1.84x)
Moana 24.18 GiB (1.01x) 2171 s (12.5x)

Most of Moana's slowdown is in Moho's read path. The Moho streaming spike shows a similar gap: strategy D (reading through Moho) takes 7.7x as long as strategy B. Part of it is in Crust: materials missing from the top index build a fresh chain, 39 times on isHibiscus alone. With full LTO, cornellbox renders about 2.5% slower than the direct binary, with no render code changed and equal digests; this is unexplained and recorded.

These are follow-ups. Moho is moving to strict Hydra parity next (one loaded stage, no chunk streaming), and update latency will be profiled before first-load time.

Checklist

  • cargo fmt --all -- --check (locally)
  • cargo clippy --workspace --all-targets -- -D warnings (locally)
  • cargo test --workspace --no-fail-fast (locally). The GitHub Rust workflow runs on this PR.
  • Both halves of any pair in docs/architecture.md § Invariants changed together (index reader ↔ in-place rebuild, added here)
  • No new unsafe, no new RNG outside openqmc, no asset decoding in crust-core
  • Logging levels follow the rules
  • User documentation in site/ updated (docs/architecture/usd-import.md; zola build OK)
  • A new CRUST_* switch: none added

Hydra workflow (hdCrust, OpenUSD 26) passed on this branch at 58b1a25a: https://github.com/doubleailes/Crust/actions/runs/40494072921

🤖 Generated with Claude Code

https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF

doubleailes and others added 30 commits October 25, 2026 16:43
Plans step S2 of the scene-index program: a second import route that reads
the stage through Moho's scene index, migrated family by family behind a
parity check against the direct import (samples, fixtures, ALab, Moana),
with the default flipped to the index route once parity holds. Pins
openusd by Moho's rev so the build carries one openusd, and lists the
Moho-side gaps (cross-chunk light linking, materials outside loaded
chunks, authored scene globals, prototype source paths) as follow-ups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
As UsdImaging's move onto scene indices was gated on parity alone
(USDIMAGINGGL_ENGINE_ENABLE_SCENE_INDEX, legacy tests kept against the same
baselines), import_parity.sh fails on report or image differences only and
prints both routes' peak memory and import time with their ratios.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
Moho's change scene-index-gaps closes the seven gaps the plan found:
light linking after the merge (chain::input / chain::merged), the
unresolved paths for the side input, stage metadata with authored-ness,
primOrigin/scenePath, strategy (D) in the spike, every pass collection,
and the merged children order. The design, tasks and parity spec now use
them: task 1.5 builds against scene-index-gaps and bumps the moho rev once
it merges, D9 keeps judging links after the last chunk with an agreement
test streamed too, and the prototype warning-path parity exception goes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
…itch

The user's call: the Crust importer can drop and move to Moho in this
change. No CRUST_USD_IMPORT switch and no default flip. Parity becomes
"against baselines recorded from today's importer" (digest, check report
and 16 spp image per sample and fixture, ALab and Moana memory and time
as a report), with a regenerate flag, as UsdImaging moved onto scene
indices. The bridge stays as a transitional device inside the change,
and a final group deletes the direct readers, the bridge, EvalTimeScope
and the prototype epoch. Specs, design and tasks revised to match;
co-development with Moho goes through `cargo --config`, since
.cargo/config.toml is tracked here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
Cargo identifies a git source by URL plus reference, so `branch = "main"`
and Moho's `rev = "68a867a…"` would be two openusd packages at one commit.
The patch now names the rev; bump order is Moho, then crust-render, then
Mantle, and `cargo update -p openusd` no longer moves it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
…openusd

crust-core gains `moho` (git, rev 92c8b84), which pins openusd at the same
rev as the workspace patch, so `cargo tree -i openusd` shows one package
with moho among its dependents. deny.toml allows Moho's source.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
tests/one_openusd.rs hands Moho's re-exported Stage, SchemaRegistry and
an openusd-schemas type to functions taking the workspace's. With the
patch set back to `branch = "main"` it failed with mismatched types, as
recorded in its doc comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
Two live tests rewrote `live_primvars.usda` while running in parallel, so
one could read the other's half-written file ("Unexpected prim specifier").
Each test now writes its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
The `parity` feature (test-only, on for crust-core's, crust-render's and
crust-hydra's tests through dev-dependencies) ends every successful
load_scene by comparing the scene's digest with a baseline under
crust-core/tests/data/import_baselines/, panicking on the first differing
line. The digest is stable text over the geometry in id order (pieces
hashed bit for bit per ray category, material sharing and Signature,
flags, side tables, motion), lights, volumes, camera, settings, AOVs,
pass, warnings, provenance, layers and Source, with paths normalised.
Baselines are keyed by the layers composed, the options, the switches and
the importing test. CRUST_RECORD_BASELINES=1 records; CRUST_PARITY_IMAGES=1
adds a 16 spp image hash (meant for --release).

crust-render/tests/import_parity.rs compares every committed sample's
`crust check --json` report, import phases removed, with its baseline.

All baselines here were recorded from the direct importer (tasks 2.1-2.3,
images still to record in a release run).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
Moho's main now carries M1 to M7 (light linking after the merge,
streaming::unresolved, stage metadata with authored-ness, primOrigin,
every pass collection, the merged children order, strategy D) and the
light-linking performance fix. The workspace builds against the git rev
with no local patch; `cargo tree -i openusd` still shows one package.
Task 1.5 done; the design's note on Moho's rev updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
scripts/parity_images.sh runs the suite optimised with
CRUST_PARITY_IMAGES=1: every import is imported again and rendered at
16 spp with the indirect clamp off, and its pixel hash compared with the
.image baseline recorded here from the direct importer. With images on,
recording writes images only (digests and check reports are compared,
never rewritten, since the second import shows in counts). The 25 tests
that observe the second import are listed in parity_images.expected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
…line

`record` keeps the check report (import phases removed), the 16 spp EXR
and the minimum peak memory and parse time over N runs; `check` compares
report and image with the current build, exits 1 on a difference, and
prints memory and time with their ratios without gating on them.
Verified on the Cornell box: check exits 0, and 1 when the report differs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
`Space` is an id interned per process in the order spaces are met, so the
provenance's StageFacts printed `Space(6)` or `Space(7)` depending on
which test ran first. The digests are re-recorded from the direct
importer with the space's name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
Streaming is scheduling only: the stage's check report and its 16 spp
image are equal with CRUST_STREAM_IMPORT=0 and without. Baselines (digests
and images) recorded from the direct importer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
…pike)

Recorded from the direct importer with scripts/import_parity.sh: ALab
10.96 GiB / 31.13 s and Moana 23.88 GiB / 173.66 s at the end of the
import; Moho's spike on Moana, (B) 1.97 GiB and (D) 2.35 GiB. The
baseline directory (reports, EXRs, goldens, a diagnostic report) is kept
in target/import-baselines/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
load_scene no longer walks the stage: a single stage is one bridge chain
(StageSceneIndex, Flattening, MaterialBindingResolving,
RenderSettingsFlattening, light linking judged by the stage), and a
streamed one is light linking over a MergingSceneIndex of the
payload-free scene input and each chunk's masked stage, added, walked
and removed in turn. The walk (index/walk.rs) keeps the direct
traversal's stack discipline, takes children from the walked input's
own chain (never the merged order above a chunk root), skips the
prototypes listed under the root, and prunes from the index: abstract and
inactive prims are absent, purpose and visibility are each prim's own
values from the generic data. Each prim's families still read through
the transitional bridge (index/bridge.rs), via visit_prim, the body
traverse_into had. Parity with the direct importer's baselines is green
on the whole suite, single-stage and streamed (tasks 3.1-3.3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
…Moho

The layers a scene composed, its root layer stack and the invisible
overrides its strongest layer authors are file and layer-spec facts, not
scene description: they move to index/stage_facts.rs, read from the
stage. The time range and whether it is authored come from the absolute
root's __usdStageMetadata (Moho's M3), on the payload-free scene input,
which load_scene now builds for every import and hands to the streamed
merging index. Parity green (task 3.4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
…spec

The fixture wrote `def Scope "Render"` and then `over "Render"` in the
same layer. openusd keeps the second specifier, so the whole /Render was
an undefined `over`: the direct importer read its prims anyway (it walked
every prim), a scene index, like UsdImaging, leaves undefined prims out.
Two specs of one prim in one layer is not valid USD; the charSettings
now sit inside the one `def`. Baselines for the corrected fixture are
recorded from the direct importer (36537fe).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
The ALab and Moana runs with the index walk matched their baselines'
images but not their reports: warnings inside prototypes named
/__Prototype_83 for _82, _1 for _0. openusd numbers a stage's prototypes
in the order they are first asked for, and the scene index's population
walk asks in another order than the direct walk did; a streamed import
numbers each chunk's stage from 0, so the number cannot be mapped
between imports. The digest comparison, the sample report test and
import_parity.sh write the digit run as `#` and compare the rest of the
path. The import-parity spec lists the exception and its reason.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
settings.rs reads the payload-free scene input instead of the index
stage: the settings prim by its Hydra type, renderSettingsPrimPath from
the root's __usdStageMetadata, the pass's renderSource and the camera and
crust:dicingCamera targets from __usdRelationships, and every attribute
(resolution, renderingColorSpace, the crust:* settings) from
__usdAttributes with the direct readers' authored-versus-fallback rule
(index/data.rs, PrimData). read_index, the live settings refresh and
`crust ls` hand it a scene input over their index stage. The USD type
named in a bad --pass error is read from the stage (stage_facts), since
the index carries Hydra types only. Parity green (tasks 3.5, 4.1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
…ndex

products.rs reads the settings prim, its products and vars from the
scene input: Hydra types for RenderSettings, RenderProduct and RenderVar,
forwarded `products` and `orderedVars` targets and every attribute from
the generic data, with Crust's own product-over-settings resolution and
its warnings kept. The precomp reads its settings relationships and the
display filters' inputs from the index; shader ids, kernel assets and
sample-filter graphs are shading networks and move with the materials.
The design records which data an index reader takes. Parity green
(task 4.2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
matte and cameraVisibility are read (one holdout, one prim hidden from
the camera); prune and renderVisibility each warn once. Baselines
recorded from the direct importer (36537fe).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
The pass's collection opinions come from the generic data (prune and
renderVisibility for the warning that they are not read), and membership
of matte and cameraVisibility is judged by the chunk's StageSceneIndex
(its CollectionMembership, openusd's query with expressions and
predicates), on the chunk being walked as before. Instances are found by
__usdPrimInfo/niPrototypePath. The walk hands the pass the input it walks.
Parity green (task 4.3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
camera.rs reads the camera prim by its Hydra type and every attribute
(projection, apertures and offsets, focal length, fStop and focus,
clipping, shutter, exposure) from the generic data, and metersPerUnit
with its authored-ness from the root's __usdStageMetadata (Moho's M3).
The walk, the read ahead of the traversal (on the scene input, or an
input over a stage masked to the camera), the first-camera search (now
a walk of the scene index), the live settings refresh and `crust ls`
all read through an index. The world transform still composes through
the bridge until the transforms move (5.1). Parity green (task 4.4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
…the index)

The settings-and-camera rebuild in live.rs goes through read_index with a
scene input over its index stage and builds the camera through an input
over the stage masked to it, so it reads what the import reads; live.rs
tests pass with the in-place versus re-import checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
materialx_lion, materialx_teapot and materialx_showcase reference
MaterialXTeapotLion-1.0, which is not committed: their check digests and
images are recorded from the direct importer (36537fe) where the files
exist, and compared there (CI skips them, as the sample test lists). The
sample report test joins the image run's expected failures: the second
import doubles the textures its report counts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
…ndex

Every mesh read takes the prim's generic data (PrimData, in the index
being walked, kept on the caches for readers reached through the bridge,
like a prototype's parts): points, counts, indices, holes, the scheme,
orientation, boundary and face-varying rules (typed reads with their
schema fallbacks), creases and corners, normals and primvars with their
indices, interpolation, elementSize, type name and colour space (the
attribute's colorSpace, then colorSpace:name up the index's ancestors),
crust:subdivisionLevel, the displacement bounds, crust:rayMask and
crust:motion:translate. Implicit shapes are told by their Hydra type
(Cylinder_1 and Capsule_1 by the radiusBottom their schema declares) and
read their sizes the same way; a sphere its radius. The live mesh and
sphere rebuilds read through an index of their stage. `crust ls` keeps
a type-only test of the shapes. Parity green on every digest and image
(tasks 6.1-6.4, 7.1, 7.2). The image script now matches only the parity
check's own panics.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
curve_segments and nurbs_curve_segments read points, counts, widths,
type, basis, order, knots, point weights and ranges from the prim's
generic data, and the strand table its primvars (values, indices,
interpolation, type name, colour space up the index's ancestors) and
the reserved primvars:curve: names. Curves in a prototype read through
the walk's index too. Parity green on every digest and on the image run
(task 8.1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
The volume dispatch and emit_volume read the region's type, noise, grid,
coefficients (colours converted from the space the attribute names, up
the index's ancestors), anisotropy, density scale and size from the
prim's generic data, in the walk and in a prototype's warning. The
material's volume terminal moves with the material networks. Parity
green (part of task 11.1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
doubleailes and others added 18 commits October 27, 2026 05:50
Moana's coral shape: a point instancer whose prototypes are prims
inside a native instance, so each target is an instance proxy. Its
baseline is the direct importer's. Moho copies such a target as an
empty prim until M12 (Moho PR #14), so the test is should_panic on the
parity check until the Moho rev that carries it; M12 is listed in the
design's open gaps, and Moana's 12.4 run waits for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
ALab's report was re-recorded for the reuse alone (unique triangles
12,007,504 to 12,001,706) and the direct importer's EXR kept; the
import_parity check passes, report and image equal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
…ances)

Moho now copies a point-instancer target that is an instance proxy from
the instance's propagated prototype. Crust already takes copies from
instancerTopology/prototypes, so it needs no change: the M12 fixture
passes, digest and image equal to the direct importer's, and M12 is
recorded closed in the design.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
A copy of a prim inside a native instance binds a material beside its
target, which the index holds in the instance's propagated prototype,
outside the copy. The copy now carries the instance (the target's
nearest ancestor the instancing chain holds as an instance, found at the
target as the chain holds it), so the material is read there and named
under the instance, as the direct importer named it through the proxy.
Moana's isCoral raised 29 material.fallback_default without it. Two
fixtures, recorded from the direct importer, cover a top-level and an
instanced instancer; both match their digests and images.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
cargo test --release (parity_images.sh) leaves a test build at
target/release/crust, with crust-core's test-only parity feature, which
digests and checks every import: a Moana check measured that build. The
script now builds crust-render first unless BIN names a binary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
EvalTimeScope and time.rs go: every stage read takes its time code
explicitly (the camera readers, stage_local_to_world, and crust ls's
value reads at its frame), and the index readers take the index's
time. The prototype and material caches are keyed by Moho's propagated
paths alone: ImportCaches::epoch and MaterialCache::epoch go, since no
root the instancing chain holds is a per-stage /__Prototype_N (probed
across the suite before removing it).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
The import reads openusd only through index/ and stage_facts.rs: the
stage binding resolution, shader ids, attribute value reads, the stage
pruning rule and the shape test, which only crust ls (walking the stage)
still calls, move into listing.rs; Prune keeps the rule's documentation.
prim_frames' authored ops (Xformable, xformOpOrder, the place op's
default) are authoring facts, read in stage_facts::authored_ops (D7).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
The import keeps no time in a thread-local (the index owns it, stage
reads take it explicitly), and prototype caches key on Moho's
propagated paths, never on openusd's per-stage /__Prototype_N.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
As import_parity.sh: a test build left at target/release/crust carries
crust-core's test-only parity feature.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
ALab and Moana pass import_parity on Moho d09cc94, their reports
re-recorded for D3c's cross-instancer reuse alone, with the spike's
(A), (B) and (D) beside them; Moana's import time (12x) is reported.
check_images is bit-identical against the direct importer's goldens
(cornellbox_guided's re-rendered, guided rendering being
nondeterministic), and crust diagnostic equals its report timings
aside. hdCrust cannot pass on this machine's glibc, before or after the
change alike; 14.1 stays open for the CI runner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
…5.2)

Moho in the crate map and the render flow, the index/ module, two new
invariants (a family's index reader and its in-place rebuild change
together; one openusd through Moho, one rev across the stack), the
epoch and EvalTimeScope rules gone; usd_import's module table names each
family's index reader, and stale doc links are fixed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
…ho (15.1)

The layout, the walk, the stage facts, the bridge's history and the rev
rule; the behaviour changes and the listed exceptions; Moho's gaps M1 to
M12 with what Crust uses from each and the rev that closed it; the
streaming and cache-keying paragraphs rewritten for the index.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
Crust reads every stage through Moho's scene index and streams a large
stage one subtree at a time; the overview names Moho beside openusd.
zola build passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
…oader

On a host whose glibc is older than the OpenUSD package needs
(anderslanglands/aswf-pixi#57) the delegate runs only through the pixi
sysroot's loader and so its libm, and a host crust differs from it by
an ulp on a few pixels. crust_sysroot.sh runs the release crust under
that same loader; the pixi tasks compare-sysroot and
interaction-sysroot point the scripts' CRUST (now overridable) at it.
CI runs the scripts unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
The Hydra workflow passes on the reference runner at 58b1a25, and
locally with the CLI under the sysroot loader; ALab and Moana pass
import_parity on the final code, their runs recorded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Read USD scenes through Moho's scene index

✨ Enhancement 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Replace direct USD prim import with one Moho scene-index route for CLI and interactive renders.
• Preserve streamed import and in-place rebuilds while migrating geometry, materials, lights,
 settings, and instancing.
• Gate the migration with recorded scene, report, and image parity baselines.
Diagram

graph TD
  A["USD stage"] --> B["Moho inputs"] --> C["Merged index"] --> D["Index walk"] --> E["Schema readers"] --> F["Render world"]
  B --> D
  A --> G["Stage facts"] --> E
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Retain both import routes behind a switch
  • ➕ Allows production fallback and direct side-by-side debugging.
  • ➖ Duplicates schema behavior and delays establishing one source for future scene-index updates.

Recommendation: Prefer the single Moho route now that the schema families have parity coverage. Keeping the direct importer would reduce short-term migration risk but impose ongoing divergence across CLI, interactive rendering, and eventual dirty-locator handling.

Files changed (45) +8672 / -3005

Enhancement (6) +1331 / -1
live.rsSupport scene-index-backed live edits +42/-1

Support scene-index-backed live edits

• Adjusts live-scene state and edit handling for index-based imports.

crates/crust-core/src/live.rs

mod.rsConstruct single and streamed Moho index chains +233/-0

Construct single and streamed Moho index chains

• Builds the single-stage chain or merges a payload-free input with masked chunks and dependencies.

crates/crust-core/src/scene/usd_import/index/mod.rs

data.rsProvide typed access to Hydra prim data +493/-0

Provide typed access to Hydra prim data

• Wraps index prims, generic attributes, relationships, and prototype path rebasing for schema readers.

crates/crust-core/src/scene/usd_import/index/data.rs

instancers.rsInterpret Moho instancer metadata +58/-0

Interpret Moho instancer metadata

• Provides shared access to propagated native and point-instancer prototype information.

crates/crust-core/src/scene/usd_import/index/instancers.rs

network.rsDecode indexed material networks +384/-0

Decode indexed material networks

• Reads Hydra material contexts, terminals, shader parameters, connections, and forwarded graph values.

crates/crust-core/src/scene/usd_import/index/network.rs

walk.rsTraverse and prune indexed prims +121/-0

Traverse and prune indexed prims

• Preserves visit order and subtree-pruning behavior while walking Moho’s children rather than direct USD prims.

crates/crust-core/src/scene/usd_import/index/walk.rs

Refactor (16) +3796 / -2886
stage_facts.rsIsolate non-imaging USD stage reads +375/-0

Isolate non-imaging USD stage reads

• Keeps layer, composition, and authored-spec facts outside schema readers while obtaining stage metadata from the index.

crates/crust-core/src/scene/usd_import/index/stage_facts.rs

mod.rsRoute scene import through Moho +571/-565

Route scene import through Moho

• Replaces direct prim traversal with index dispatch and coordinates single-stage, streamed, and cached import state.

crates/crust-core/src/scene/usd_import/mod.rs

attrs.rsReuse typed decoders for indexed attributes +4/-222

Reuse typed decoders for indexed attributes

• Removes direct attribute-reading helpers now supplied by indexed generic data.

crates/crust-core/src/scene/usd_import/attrs.rs

camera.rsRead cameras from scene-index schemas +142/-87

Read cameras from scene-index schemas

• Moves camera properties and transforms to indexed data while retaining renderer camera behavior.

crates/crust-core/src/scene/usd_import/camera.rs

implicit.rsRead implicit shapes through Moho +73/-76

Read implicit shapes through Moho

• Migrates cube, plane, cylinder, cone, and capsule inputs from typed USD prims to index data.

crates/crust-core/src/scene/usd_import/implicit.rs

instancing.rsImport propagated and point-instancer prototypes +341/-189

Import propagated and point-instancer prototypes

• Uses Moho instancer data and propagated paths for prototype reuse and placement.

crates/crust-core/src/scene/usd_import/instancing.rs

light_links.rsResolve indexed light and shadow links +227/-72

Resolve indexed light and shadow links

• Uses Moho’s collection results across merged inputs and finalizes linking after streamed chunks.

crates/crust-core/src/scene/usd_import/light_links.rs

lights.rsImport lights from Hydra data +167/-148

Import lights from Hydra data

• Moves light attributes, shaping, and visibility reads to indexed schemas and generic data.

crates/crust-core/src/scene/usd_import/lights.rs

listing.rsSeparate stage listing from scene import +373/-61

Separate stage listing from scene import

• Moves stage inspection and listing helpers out of the importer’s main traversal.

crates/crust-core/src/scene/usd_import/listing.rs

live.rsRebuild live scenes through indexed readers +96/-74

Rebuild live scenes through indexed readers

• Updates masked-stage rebuilds to use Moho prim data and removes direct schema and time-scope reads.

crates/crust-core/src/scene/usd_import/live.rs

materials.rsResolve materials using indexed bindings +614/-639

Resolve materials using indexed bindings

• Migrates material lookup, caching, and shader dispatch to Hydra material bindings and networks.

crates/crust-core/src/scene/usd_import/materials.rs

mesh.rsRead mesh geometry from scene-index data +111/-141

Read mesh geometry from scene-index data

• Moves mesh topology, primvars, and geometry inputs off direct USD mesh schemas.

crates/crust-core/src/scene/usd_import/mesh.rs

mtlx_network.rsConsume indexed MaterialX networks +183/-288

Consume indexed MaterialX networks

• Builds MaterialX documents from indexed shader nodes and connections.

crates/crust-core/src/scene/usd_import/mtlx_network.rs

preview.rsConsume indexed USD Preview networks +259/-118

Consume indexed USD Preview networks

• Migrates preview-surface and texture graph handling to Hydra network data.

crates/crust-core/src/scene/usd_import/preview.rs

xform.rsEvaluate indexed transforms and motion +260/-153

Evaluate indexed transforms and motion

• Uses index transforms and explicit time handling, including shutter samples, instead of direct prim evaluation.

crates/crust-core/src/scene/usd_import/xform.rs

time.rsRemove the importer’s time scope +0/-53

Remove the importer’s time scope

• Deletes EvalTimeScope because indexed reads carry their own evaluation time.

crates/crust-core/src/scene/usd_import/time.rs

Documentation (6) +1554 / -81
architecture.mdDocument Moho’s place in rendering +35/-16

Document Moho’s place in rendering

• Updates the crate map, import flow, index layout, and USD dependency invariants.

docs/architecture.md

usd-import.mdDocument the scene-index importer +75/-0

Document the scene-index importer

• Adds a site architecture page describing Moho-based USD import.

site/content/docs/architecture/usd-import.md

design.mdRecord migration design and parity decisions +619/-0

Record migration design and parity decisions

• Documents index-chain choices, streaming, prototype handling, accepted differences, and validation.

openspec/changes/moho-import/design.md

proposal.mdPropose the Moho import migration +108/-0

Propose the Moho import migration

• States the motivation, scope, and intended outcome of the scene-index step.

openspec/changes/moho-import/proposal.md

tasks.mdTrack the importer migration +375/-0

Track the importer migration

• Records family-by-family implementation and validation tasks.

openspec/changes/moho-import/tasks.md

design.mdUpdate the USD import design record +342/-65

Update the USD import design record

• Replaces direct-import assumptions with scene-index architecture and documented behavioral exceptions.

openspec/specs/usd-scene-import/design.md

Other (17) +1991 / -37
Cargo.tomlPin Moho as the workspace USD dependency +10/-16

Pin Moho as the workspace USD dependency

• Replaces direct openusd workspace dependencies and patches with a pinned Moho dependency and its openusd re-exports.

Cargo.toml

Cargo.lockLock the Moho dependency graph +16/-9

Lock the Moho dependency graph

• Records the Moho pin and corresponding openusd dependency changes.

Cargo.lock

Cargo.tomlWire Moho and parity into crust-core +19/-12

Wire Moho and parity into crust-core

• Moves importer dependencies through Moho and configures test-only parity support.

crates/crust-core/Cargo.toml

lib.rsExpose import parity under its feature +3/-0

Expose import parity under its feature

• Registers the parity module for test-enabled builds.

crates/crust-core/src/lib.rs

parity.rsCompare each test import with direct-import baselines +502/-0

Compare each test import with direct-import baselines

• Adds normalized scene digests and optional 16-spp image hashes, with baseline recording and comparison.

crates/crust-core/src/parity.rs

rt_world.rsExpose stable render-world parity facts +121/-0

Expose stable render-world parity facts

• Adds world and geometry inspection needed to construct deterministic import digests.

crates/crust-core/src/rt_world.rs

import_parity.rsCheck sample-scene import reports +290/-0

Check sample-scene import reports

• Compares normalized 'crust check --json' results against direct-import baselines for committed samples.

crates/crust-render/tests/import_parity.rs

instanced_instancers.rsCover nested instancer combinations +483/-0

Cover nested instancer combinations

• Adds regression tests for prototype and instancer interactions under the Moho import route.

crates/crust-core/tests/instanced_instancers.rs

one_openusd.rsEnforce one openusd dependency +54/-0

Enforce one openusd dependency

• Fails if a crate depends on openusd directly rather than through Moho.

crates/crust-core/tests/one_openusd.rs

streamed_light_links.rsTest linking across streamed chunks +72/-0

Test linking across streamed chunks

• Exercises light-link behavior when linked prims lie outside the current chunk.

crates/crust-core/tests/streamed_light_links.rs

streamed_materials.rsTest materials outside streamed chunks +121/-0

Test materials outside streamed chunks

• Covers bindings and shader dependencies resolved through merged side inputs.

crates/crust-core/tests/streamed_materials.rs

xform_uncomposable.rsCover unusual transform stacks +50/-0

Cover unusual transform stacks

• Adds regression coverage for transforms the indexed representation cannot compose conventionally.

crates/crust-core/tests/xform_uncomposable.rs

import_parity.shCompare large-scene reports and EXRs +132/-0

Compare large-scene reports and EXRs

• Records or checks ALab and Moana parity, reporting import memory and time without gating on performance.

scripts/import_parity.sh

parity_images.shRun sample image-parity checks +57/-0

Run sample image-parity checks

• Automates image comparison against the committed expected parity set.

scripts/parity_images.sh

parity_images.expectedRecord expected image-parity results +28/-0

Record expected image-parity results

• Lists the expected image comparisons used by the parity script.

scripts/parity_images.expected

crust_sysroot.shRun the CLI under pixi’s glibc sysroot +18/-0

Run the CLI under pixi’s glibc sysroot

• Makes the comparison CLI use the same loader and libm as hdCrust on older-glibc hosts.

hydra/hdCrust/test/crust_sysroot.sh

pixi.tomlAdd sysroot comparison tasks +15/-0

Add sysroot comparison tasks

• Provides compare-sysroot and interaction-sysroot tasks for older-glibc Linux hosts.

hydra/pixi.toml

@qodo-code-review

qodo-code-review Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. A sample import mismatch can pass the image gate ✓ Resolved
Description
parity_images.sh excludes every failure and parity diagnostic from tests named in
parity_images.expected, regardless of why the test failed. That list includes
every_sample_imports_as_its_baseline, so a baseline mismatch in the sample-import test can be
suppressed and the gate can report success.
Code

scripts/parity_images.expected[17]

+every_sample_imports_as_its_baseline
Relevance

●●● Strong

Baseline-gate precedents accept fixes preventing missing or failed comparisons from passing
silently.

PR-#174

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The script filters parity diagnostics and failed-test names using the expected list before deciding
its status. The listed sample test performs baseline comparisons, so its actual mismatches fall
under both filters.

scripts/parity_images.sh[32-55]
scripts/parity_images.expected[17-17]
crates/crust-render/tests/import_parity.rs[138-180]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The image runner treats all failures in listed tests as expected, including import-parity failures in the sample baseline test.
## Fix Focus Areas
- scripts/parity_images.expected[17-17]
- scripts/parity_images.sh[34-52]
## Recommended Fix
Make any baseline or parity mismatch fail the runner regardless of test name. Apply the expected-test allowance only to the specific unrelated failure for which each test is listed.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Referenced-layer textures load from the wrong directory ✓ Resolved
Description
asset_value_path anchors an unresolved relative asset path to the root stage directory, replacing
the authoring-layer lookup previously used by asset_path. When a sublayer or reference authors a
path such as a UDIM texture with no resolved path, preview and MaterialX readers can request a
nonexistent or different file.
Code

crates/crust-core/src/scene/usd_import/preview.rs[372]

+    let file = value(tk::FILE).and_then(|v| asset_value_path(&v, caches.stage_path));
Relevance

●●● Strong

Recent importer precedents accept fixes for incorrect asset resolution and unresolved texture
handling.

PR-#194
PR-#333

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new texture reader passes only the value and root stage path to a helper whose fallback joins
that path's parent. The removed helper used the attribute property stack's first authoring layer for
unresolved relative values; the MaterialX reader also uses the new helper.

crates/crust-core/src/scene/usd_import/preview.rs[366-372]
crates/crust-core/src/scene/usd_import/assets.rs[153-175]
crates/crust-core/src/scene/usd_import/mtlx_network.rs[343-344]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Unresolved relative texture assets are now anchored to the root stage rather than the layer that authored their attribute.
## Fix Focus Areas
- crates/crust-core/src/scene/usd_import/preview.rs[366-372]
- crates/crust-core/src/scene/usd_import/mtlx_network.rs[343-344]
- crates/crust-core/src/scene/usd_import/assets.rs[153-175]
## Recommended Fix
Preserve the authoring layer for unresolved asset parameters and resolve relative paths against it. Use the root stage directory only when that layer cannot be determined, and cover a UDIM path authored in a referenced or sublayered file.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Parity image run passes when the build fails ✓ Resolved
Description
scripts/parity_images.sh runs cargo test --release --workspace --no-fail-fast without checking
its exit status, then determines success solely from parity: panics and test … FAILED lines in
the log. If compilation fails, Cargo stops before tests run, or a test binary aborts before printing
results, neither pattern appears, so the script prints “every import renders as its baseline” and
exits 0, making the parity gate look green.
Code

scripts/parity_images.sh[R29-32]

+CARGO_PROFILE_RELEASE_LTO=false CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16 \
+    cargo test --release --workspace --no-fail-fast >"$LOG" 2>&1
+
+status=0
Relevance

●●● Strong

This is a deterministic gate bug: ignored Cargo failure status can falsely produce success.

PR-#174

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Line 20 uses set -uo pipefail without -e, and lines 29–30 run Cargo without checking its exit
status. Line 32 initializes status=0; lines 33–53 change it only when parity-panic text or
failed-test lines appear, and lines 54–57 report success otherwise. A failure before test results
are printed produces neither match, so the script exits 0.

scripts/parity_images.sh[20-57]
scripts/parity_images.sh[20-32]
scripts/parity_images.sh[34-57]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`scripts/parity_images.sh` can report parity success when `cargo test` fails before executing tests or before printing test results.

## Fix Focus Areas
- scripts/parity_images.sh[29-57]

## Recommended Fix
Capture the Cargo exit status, for example with `cargo test ... >"$LOG" 2>&1; cargo_status=$?`, and require evidence that the intended test run completed. Preserve the handling of identified test failures, but if Cargo exits nonzero without `test ... FAILED` lines, print the tail of `$LOG` and set `status=1`. Also consider failing when the log contains no `test result:` line.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
❌ Tickets: failed to load — Ticket collection could not establish complete evidence
✅ Cross-repo context — repo relationships
  Explored: repo: doubleailes/OpenSubdiv-rs (sha: 74f2199c) — View relationship
  Explored: repo: doubleailes/Mantle (sha: 4d89ab35) — View relationship
  Explored: repo: doubleailes/Obsidian (sha: 7e84febb) — View relationship
Review mode: Auto: 🧠 Deep: Massive cross-cutting importer rewrite spans many independent logic paths.

Grey Divider

Tip of the day
💡 Did you know, you can describe a rule in plain language on the Rules page and Qodo drafts it for you

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

doubleailes and others added 3 commits October 27, 2026 15:52
… assets are absent

materialx_lion, _teapot and _showcase reference the DPEL MaterialX
assets by absolute path, outside the repository (not committed). Where
those files exist the sample is compared against its recorded baseline
as before; where they do not (CI), the composed layers differ, no
baseline matches, and the test now skips it with the paths it needs, as
crust-render's import_parity.rs already does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
… failed build

Review of PR #415 (Qodo): a test listed in parity_images.expected had
all of its failures excused, so a listed test (the sample report test
among them) could hide a baseline mismatch; and a build error or a test
binary dying before its summary left no FAILED line and passed.

Now every parity: diagnostic fails the run, whichever test or crust
process raised it, but an image difference of a stage listed as
stage:<file> (cornellbox_guided.usda, nondeterministic guided render;
its digest is still compared); a listed test's failure is excused, its
import differences are not; a build error, a test target with no
result, or cargo failing without a failed test fails the run.
every_sample_imports_as_its_baseline is no longer listed: in the image
run it still runs every sample's check (digest and image compared in
the crust process) and only skips the report comparison.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
Review of PR #415 (Qodo): the index readers anchored an asset value
openusd had not resolved on the root layer's directory, where the
direct importer used the attribute's authoring layer. openusd anchors
an asset value on its layer and records where it resolves even with no
file there (a UDIM set, a missing texture), so asset-typed paths were
right; a path authored as a string was not. asset_value_path now takes
the authoring layer's directory, read in stage_facts
(authoring_layer_dir, following an interface connection), at every
call site: UsdUVTexture and Pxr Ptex files, MaterialX filenames, the
precomp kernel, IES, rect and dome light textures.

tests/asset_anchoring.rs (recorded from the direct importer): assets in
a referenced layer two directories below the root, a UDIM set, a
missing file, a MaterialX filename and a light texture, and a
string-typed texture path, which failed before this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
@doubleailes

Copy link
Copy Markdown
Owner Author

Replies to the Qodo review (comment 6444723949). Each finding was verified against the code first.

1. A sample import mismatch can pass the image gate: real, fixed in ab55f84.

  • A test listed in parity_images.expected had all of its failures excused, including baseline mismatches.
  • Now every parity: diagnostic fails the run, whichever test or crust process raised it. The one exception is an image difference of a stage listed as stage:<file>: only cornellbox_guided.usda, whose guided render is nondeterministic. Its digest is still compared.
  • A listed test's own failure is still excused, but never its import differences.
  • every_sample_imports_as_its_baseline is no longer listed. In the image run it still runs every sample's check, which compares the digest and image in the crust process, and only skips the report comparison. The second import doubles the texture counts that comparison would see.

2. Referenced-layer textures load from the wrong directory: real for string-typed paths, fixed in b5ee1de. Details are in the reply on the inline comment.

3. The parity image run passes when the build fails: real, fixed in ab55f84.
The run now fails on any of these:

  • a build error;
  • a test target that reports no test result line, for example a binary that aborted;
  • cargo exiting non-zero with no failed test.

I checked the script against synthetic logs:

  • listed failures plus a guided image difference: passes;
  • a guided digest difference: fails;
  • a parity mismatch inside a listed test: fails;
  • a compile error: fails;
  • an aborted test binary: fails.

The real run (scripts/parity_images.sh) passes on b5ee1de.

Also on this branch, f58c950 fixes the CI test failure. sample_imports_are_deterministic now skips, with a message, a sample whose absolute external references are absent; on CI that is the uncommitted DPEL MaterialX assets. Samples whose assets are present are still compared, the same way import_parity.rs already skips them.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF

@doubleailes
doubleailes merged commit 0bebb34 into main Oct 27, 2026
15 checks passed
doubleailes added a commit that referenced this pull request Oct 27, 2026
… failed build

Review of PR #415 (Qodo): a test listed in parity_images.expected had
all of its failures excused, so a listed test (the sample report test
among them) could hide a baseline mismatch; and a build error or a test
binary dying before its summary left no FAILED line and passed.

Now every parity: diagnostic fails the run, whichever test or crust
process raised it, but an image difference of a stage listed as
stage:<file> (cornellbox_guided.usda, nondeterministic guided render;
its digest is still compared); a listed test's failure is excused, its
import differences are not; a build error, a test target with no
result, or cargo failing without a failed test fails the run.
every_sample_imports_as_its_baseline is no longer listed: in the image
run it still runs every sample's check (digest and image compared in
the crust process) and only skips the report comparison.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
@doubleailes
doubleailes deleted the openspec/moho-import branch October 27, 2026 16:08
doubleailes added a commit that referenced this pull request Oct 27, 2026
The import through Moho is merged (#415): archive the change and sync its
delta specs, creating the scene-index-import and import-parity specs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUSvMHN9Akk66u2oG3rUbF
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