Skip to content

crust-core: the delegate asks its host only for .mtlx references (host-facts-mtlx-only) - #432

Merged
doubleailes merged 3 commits into
mainfrom
host-facts-mtlx-only
Oct 30, 2026
Merged

doubleailes merged 3 commits into
mainfrom
host-facts-mtlx-only

Conversation

@doubleailes

Copy link
Copy Markdown
Owner

Why

Since #431 (hydra-only-renders), Crust's image depends on the scene index alone. The delegate still asked its host eight more questions about the stage through HostFacts:

  • whether a time range is authored;
  • the composed layers;
  • the root layer stack;
  • invisible overrides;
  • why a pass is missing;
  • a material's foreign spec paths;
  • the transform facts behind three xform.* warnings;
  • whether an instance lacks a prototype.

A Hydra 2.0 delegate is handed a terminal scene index and nothing else. The one fact Hydra cannot carry is the .mtlx document a material references, which openusd does not compose until mtlx-format-plugin.

What

  • HostFacts keeps one method, mtlx_reference. crust-core/tests/host_facts_mtlx_only.rs implements it with one method. BREAKING (API): StageHostFacts::new takes the stage only.
  • Read from the index:
    • time.outside_range takes sceneGlobals/startTimeCode and endTimeCode. 0..0, what the index gives a stage that authors none, means no range, so a stage authoring 0..0 itself gets no warning.
    • A pass that is not a RenderPass is explained by the index: no prim there, or the prim's type. That is its USD type name from __usdPrimInfo/typeName, else its Hydra type, so an Xform is still named Xform.
  • BREAKING (report): the warnings xform.unknown_op, xform.unknown_type, xform.uncomposable and instancing.instanceable_without_prototype are removed. The index carries none of their causes. Removing codes bumps crust check --json to crust-check/3, under the scene-warnings versioning rule.
  • The stamp is the batch host's. crust:layerStack and crust:invisibleOverrides come from the stage crust render opened. load_scene_with reads them before the commit releases the stage and sets them on the scene after it. The stamp's content is unchanged.
  • Removed: Scene::layers, which nothing read, with tests/usd_layers.rs. Also the foreign spec paths in material provenance.
  • Guard test: crust-render/tests/host_facts.rs leaves out cornellbox_guided. Path guiding is not bit-reproducible run to run: on the same binary, about one 16 spp render in five differs. That race predates this change and makes the guard flaky. It is not fixed here.

OpenSpec: openspec/changes/host-facts-mtlx-only/.

  • render-delegate: "The image depends on the scene index alone" is modified. Two requirements are added: "The delegate's warnings come from the index" and "The batch host stamps the stage's layers".
  • scene-check: crust-check/3.

Tests

  • New: host_facts_mtlx_only.rs, four tests covering the one question, an authored and an unauthored range, and a pass naming a mesh or nothing.
  • Reworked: the tests of the removed warnings now check that importing those scenes raises nothing. xform_uncomposable.rs, warnings.rs and the Once-policy unit test use another Once code.

Measurements

  • Images: scripts/check_images.sh against goldens recorded before crust-core: the image depends on the scene index alone (hydra-only-renders) #431 (16 spp, --indirect-clamp 0): all 45 samples identical. Kitchen_set is not on the machine I used.
  • Local gate (Windows):
    • fmt is clean; clippy is clean, with and without --features delegate.
    • The workspace tests pass except three that fail identically on unmodified main, all Windows-specific: kept (touching a directory is denied), asset_anchoring (Linux path expectations) and stamp (8.3 path spelling).
    • Linux CI is the check for those. In particular, stamp confirms the layer stack is unchanged (task 3.2).

Downstream

At its next crust-render bump, Mantle replaces StageHostFacts::new(&stage, &path, Some(terminal)) with StageHostFacts::new(&stage) (its ipr-index-only).

🤖 Generated with Claude Code

…t-facts-mtlx-only)

HostFacts keeps mtlx_reference alone (a test implements it with one
method); StageHostFacts::new takes the stage only. Everything else the
delegate reads, warns about or reports comes from the scene index:

- time.outside_range reads sceneGlobals start/endTimeCode, 0..0 being no
  range; a pass that is not a RenderPass is explained from the index (no
  prim, or its type from __usdPrimInfo, else its Hydra type).
- Removed: xform.unknown_op, xform.unknown_type, xform.uncomposable and
  instancing.instanceable_without_prototype (the index carries none of
  their causes), so crust check writes crust-check/3; Scene::layers
  (unused, with tests/usd_layers.rs); foreign spec paths in material
  provenance.
- The EXR stamp's layer stack and invisible-override count are the batch
  host's: load_scene_with reads them off its stage before the commit and
  sets them after it.
- crust-render/tests/host_facts.rs leaves cornellbox_guided out: path
  guiding is not bit-reproducible run to run (about 1 render in 5).

Images: all 45 samples identical to the goldens recorded before
hydra-only-renders (16 spp, --indirect-clamp 0).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Limit delegate host facts to MaterialX references

✨ Enhancement 📝 Documentation 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Limit delegate stage queries to MaterialX references; derive time and pass diagnostics from the
 scene index.
• Move EXR stamp facts to the batch host and retire warnings the index cannot explain.
• Remove unused layer reporting and bump the check JSON format to crust-check/3.
Diagram

graph TD
  Stage[("USD stage")] --> Batch["Batch host"] --> Index["Scene index"] --> Delegate["Hydra delegate"] --> Scene["Committed scene"] --> Stamp[("EXR stamp")]
  Batch -->|mtlx reference| Delegate
  Batch -->|stamp facts| Scene
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Keep stage-only diagnostics in the batch host
  • ➕ Could preserve transform and prototype warnings for batch imports.
  • ➖ Warnings would differ between batch and index-only hosts.
  • ➖ It would complicate the contract that delegate diagnostics come from the index.

Recommendation: Use the PR's separation: index-derived delegate diagnostics and stage-owned batch stamping give index-only hosts a clear contract. Host-specific diagnostics could be added separately if preserving the retired warnings becomes a requirement.

Files changed (41) +657 / -493

Enhancement (2) +45 / -10
check.rsVersion check reports as crust-check/3 +7/-7

Version check reports as crust-check/3

• Changes the report format constant, documentation, and contract assertion after warning codes are retired.

crates/crust-core/src/check.rs

settings.rsDerive time and pass diagnostics from the index +38/-3

Derive time and pass diagnostics from the index

• Explains invalid passes using index prim types and reads time bounds from sceneGlobals, treating 0..0 as no range.

crates/crust-core/src/scene/hydra/settings.rs

Refactor (13) +56 / -402
lib.rsRemove the XformFacts public export +2/-2

Remove the XformFacts public export

• Stops exporting the removed host transform-facts type and updates the check module documentation.

crates/crust-core/src/lib.rs

scene.rsRemove unused composed-layer scene data +1/-7

Remove unused composed-layer scene data

• Drops Scene::layers and its initialization, and stops re-exporting XformFacts.

crates/crust-core/src/scene.rs

commit.rsStop stage-dependent prim diagnostics +1/-9

Stop stage-dependent prim diagnostics

• Removes host-backed transform and missing-prototype warnings from prim commits.

crates/crust-core/src/scene/hydra/commit.rs

host.rsReduce HostFacts to MaterialX references +9/-64

Reduce HostFacts to MaterialX references

• Removes eight stage-fact methods and XformFacts; hosts now answer only mtlx_reference.

crates/crust-core/src/scene/hydra/index/host.rs

instancing.rsRemove host-backed prototype warning +0/-8

Remove host-backed prototype warning

• Stops querying the host for instances without prototypes while collecting prototype parts.

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

materials.rsUse index-visible material dependencies +6/-16

Use index-visible material dependencies

• Removes foreign layer-spec paths from material provenance reads, retaining external network nodes named by the index.

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

mod.rsCommit index-derived diagnostics without stamp facts +7/-8

Commit index-derived diagnostics without stamp facts

• Reads the time range and pass explanation from the index and leaves stage-owned stamp fields unset for the batch host to fill.

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

update.rsRefresh settings without host facts +1/-1

Refresh settings without host facts

• Uses the index-only settings reader during in-place updates.

crates/crust-core/src/scene/hydra/update.rs

xform.rsRemove stage-backed transform diagnostics +3/-81

Remove stage-backed transform diagnostics

• Deletes transform warning checks whose causes are unavailable from the composed index matrix.

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

mod.rsApply stamp facts after the batch commit +11/-3

Apply stamp facts after the batch commit

• Captures layer-stack files and invisible-override count before stage release, then places them on the committed scene.

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

stage_facts.rsKeep only needed batch stage facts +13/-193

Keep only needed batch stage facts

• Simplifies StageFacts construction and its HostFacts implementation to MaterialX references. Retains standalone stage helpers used for stamping and batch checks.

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

warnings.rsRetire four unavailable warning codes +1/-9

Retire four unavailable warning codes

• Removes three transform codes and the missing-prototype code; adjusts a warning test to use a remaining code.

crates/crust-core/src/warnings.rs

update_latency.rsAdapt latency example to simplified facts +1/-1

Adapt latency example to simplified facts

• Constructs StageHostFacts using only the stage.

crates/crust-render/examples/update_latency.rs

Tests (10) +196 / -44
implicit.rsRead implicit settings without host facts +1/-8

Read implicit settings without host facts

• Updates the test helper for the index-only read_index signature.

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

mod.rsAdapt delegate test setup to StageHostFacts +1/-1

Adapt delegate test setup to StageHostFacts

• Constructs stage facts with the stage alone.

crates/crust-core/tests/common/mod.rs

host_facts_mtlx_only.rsGuard the one-question host contract +143/-0

Guard the one-question host contract

• Adds tests for a one-method HostFacts implementation, index-derived time warnings, and invalid-pass explanations with NoHostFacts.

crates/crust-core/tests/host_facts_mtlx_only.rs

lens_units_from_index.rsAdapt lens test host setup +1/-1

Adapt lens test host setup

• Uses the stage-only StageHostFacts constructor.

crates/crust-core/tests/lens_units_from_index.rs

warnings.rsAssert removed transform warnings stay absent +21/-7

Assert removed transform warnings stay absent

• Replaces expectations for unknown transform-op and unknown-type warnings with absence checks.

crates/crust-core/tests/warnings.rs

xform_uncomposable.rsAssert uncomposable transforms import silently +11/-16

Assert uncomposable transforms import silently

• Changes the test from expecting a stage-derived warning to expecting no warning for the affected prim.

crates/crust-core/tests/xform_uncomposable.rs

mod.rsAdapt delegate test host setup +1/-1

Adapt delegate test host setup

• Uses the simplified StageHostFacts constructor.

crates/crust-render/src/delegate/mod.rs

check.rsAssert check JSON format v3 +6/-6

Assert check JSON format v3

• Updates CLI tests to expect crust-check/3 on stdout and in report files.

crates/crust-render/tests/check.rs

delegate.rsAdapt render delegate test hosts +2/-2

Adapt render delegate test hosts

• Updates test fixtures to construct StageHostFacts from the stage alone.

crates/crust-render/tests/delegate.rs

host_facts.rsExclude nondeterministic guided sample +9/-2

Exclude nondeterministic guided sample

• Leaves cornellbox_guided out of the bitwise host-facts comparison because path-guiding training varies between runs.

crates/crust-render/tests/host_facts.rs

Documentation (16) +360 / -37
CLAUDE.mdDocument check format v3 +1/-1

Document check format v3

• Updates the command example to identify the crust-check/3 JSON report.

CLAUDE.md

lib.rsDescribe check JSON format v3 +2/-2

Describe check JSON format v3

• Updates CLI help and import_checked documentation to name crust-check/3.

crates/crust-render/src/lib.rs

main.rsUpdate check command format documentation +1/-1

Update check command format documentation

• Names crust-check/3 in the check command description.

crates/crust-render/src/main.rs

architecture.mdDescribe the narrowed host boundary +2/-2

Describe the narrowed host boundary

• Documents MaterialX as the sole delegate host question and assigns layer-stack stamping to the batch host.

docs/architecture.md

.openspec.yamlRegister the spec-driven change +2/-0

Register the spec-driven change

• Adds schema and creation metadata for the change.

openspec/changes/host-facts-mtlx-only/.openspec.yaml

design.mdRecord host-fact design decisions +96/-0

Record host-fact design decisions

• Explains the index-only diagnostics boundary, batch-owned stamp, report version, alternatives, and trade-offs.

openspec/changes/host-facts-mtlx-only/design.md

proposal.mdPropose MaterialX-only host facts +74/-0

Propose MaterialX-only host facts

• Sets out the motivation, affected capabilities, compatibility changes, and expected impact.

openspec/changes/host-facts-mtlx-only/proposal.md

spec.mdSpecify delegate warnings and batch stamping +84/-0

Specify delegate warnings and batch stamping

• Requires a single MaterialX host question, index-derived diagnostics, and stage-derived stamp fields.

openspec/changes/host-facts-mtlx-only/specs/render-delegate/spec.md

spec.mdSpecify the crust-check/3 report +26/-0

Specify the crust-check/3 report

• Sets the JSON format and stdout/file behavior for check reports.

openspec/changes/host-facts-mtlx-only/specs/scene-check/spec.md

tasks.mdTrack migration and verification work +28/-0

Track migration and verification work

• Records implementation and test tasks, including the remaining stamp verification task.

openspec/changes/host-facts-mtlx-only/tasks.md

design.mdUpdate CLI report version history +8/-5

Update CLI report version history

• Documents crust-check/3 and the retired warnings that prompted the format bump.

openspec/specs/cli/design.md

design.mdUpdate delegate host-fact design record +20/-9

Update delegate host-fact design record

• Explains the sole MaterialX exception, index-derived diagnostics, batch stamping, and guided-render test exclusion.

openspec/specs/render-delegate/design.md

design.mdRecord import behavior after warning removal +14/-11

Record import behavior after warning removal

• Updates host-fact and transform documentation to reflect index-derived behavior and silent unsupported transform cases.

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

command-line.mdAdvertise check JSON format v3 +1/-1

Advertise check JSON format v3

• Updates the --json command reference to name crust-check/3.

site/content/docs/reference/command-line.md

warnings.mdRemove retired warning references +0/-4

Remove retired warning references

• Deletes documentation rows for the three transform warnings and missing-prototype warning.

site/content/docs/reference/warnings.md

geometry.mdClarify silent transform fallbacks +1/-1

Clarify silent transform fallbacks

• Explains that unsupported transform cases no longer raise warnings because the index exposes only the composed transform.

site/content/docs/usd/geometry.md

@qodo-code-review

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

Copy link
Copy Markdown
Contributor

Code Review by Qodo

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

Grey Divider


Remediation recommended

1. Frames warn falsely when only one time end is set ✓ Resolved
Description
index_time_range reads startTimeCode and endTimeCode separately and treats any pair except
(0, 0) as an authored range. If a stage authors only startTimeCode = 1001, the index fills in
USD's fallback 0 for the end, giving (1001, 0), and check_time_range then raises
time.outside_range for every positive frame. The old gate, has_authored_time_code_range, counted
a range as authored only when both ends were set.
Code

crates/crust-core/src/scene/hydra/settings.rs[R607-611]

+    let range = (
+        field(globals::START_TIME_CODE)?,
+        field(globals::END_TIME_CODE)?,
+    );
+    (range != (0.0, 0.0)).then_some(range)
Relevance

●●● Strong

Clear correctness bug causing false warnings; history favors validating malformed or incomplete
authored settings.

PR-#224
PR-#368

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new function returns the pair whenever it is not exactly (0,0). check_time_range warns when
time > end, so an end of 0 makes every normal frame look out of range. The removed code returned
None unless the stage said it authored a range. The PR's design describes only the full 0..0 case
as a narrower warning and does not cover a range with one end missing.

crates/crust-core/src/scene/hydra/settings.rs[600-626]
crates/crust-core/src/scene/hydra/mod.rs[460-462]
openspec/changes/host-facts-mtlx-only/design.md[52-56]

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

## Issue description
`index_time_range` returns a range such as `(1001, 0)` when a stage authors only one of `startTimeCode`/`endTimeCode`. As a result, every frame raises `time.outside_range`.

## Fix Focus Areas
- crates/crust-core/src/scene/hydra/settings.rs[600-611]

## Recommended Fix
If Moho exposes whether each end is authored (`__usdStageMetadata/.../authored`), use that flag. Otherwise, return `None` when `start > end` (no valid range). Also return `None` when either end is still at the 0 fallback while the other is not. Add a test with only `startTimeCode = 1001` authored, rendered at frame 1010, that expects no warning.

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



Informational

2. The frame-range warning's doc lands on the wrong function ✓ Resolved
Description
The new index_time_range and its doc comment were added between check_time_range's existing
/// doc block and its fn. So rustdoc joins the two blocks into index_time_range's doc, and
check_time_range has none. A reader of index_time_range is told it "Warns when time lies
outside the stage's authored range", which it never does.
Code

crates/crust-core/src/scene/hydra/settings.rs[R596-600]

+/// The stage's `startTimeCode` .. `endTimeCode`, from the absolute root's
+/// `sceneGlobals` in `scene`; `None` for `0..0`, what the index gives a stage
+/// that authors no range (USD's fallback), so a stage authoring `0..0`
+/// itself reads as unauthored too.
+pub(in crate::scene) fn index_time_range(scene: &dyn SceneIndex) -> Option<(f64, f64)> {
Relevance

●●● Strong

Deterministic rustdoc attachment error; nearby history accepts documentation corrections and
comment-placement fixes.

PR-#192
PR-#315

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Lines 591-595 are the old check_time_range doc. Lines 596-599 add the new doc right after them,
and line 600 is fn index_time_range. check_time_range at line 614 now has no doc.

crates/crust-core/src/scene/hydra/settings.rs[591-614]

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

## Issue description
`index_time_range` was inserted between `check_time_range`'s doc comment and its signature, so the two doc blocks merge.

## Fix Focus Areas
- crates/crust-core/src/scene/hydra/settings.rs[591-614]

## Recommended Fix
Move `index_time_range` and its own doc comment above the `/// Warns when time lies outside...` block, so that block once again sits directly above `check_time_range`.

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


Grey Divider

Context sources
✅ Linked PRs:
  🌐 PR #431
✅ Cross-repo context — repo relationships
  Explored: repo: doubleailes/Obsidian (sha: fa365887) — View relationship
  Explored: repo: doubleailes/Mantle (sha: 566eb0d1) — View relationship
Review mode: Auto: 🧠 Deep: Broad cross-cutting behavior, API, reporting, stamping, and warning changes create dense independent risks.

Grey Divider

Tip of the day
💡 Did you know, you can route each severity your way: inline, summary, both, or drop

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

doubleailes and others added 2 commits October 30, 2026 11:48
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…odo on #432)

- index_time_range: the index gives an unauthored end as USD's fallback 0
  and does not say which ends are authored, so a start after its end (a
  start authored alone, e.g. 1001..0) reads as no range, as 0..0 does,
  instead of warning on every frame. An end authored alone reads as 0..end
  (recorded in the design and the render-delegate design record).
  tests/host_facts_mtlx_only.rs: a_start_authored_alone_raises_nothing.
- check_time_range's doc comment sits above check_time_range again; it had
  merged into index_time_range's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@doubleailes
doubleailes merged commit 579a2f9 into main Oct 30, 2026
15 checks passed
doubleailes added a commit that referenced this pull request Oct 30, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@doubleailes
doubleailes deleted the host-facts-mtlx-only branch October 30, 2026 11:24
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