# Namespace Editing and inputs connected to ancestors

**URL:** <https://forum.aousd.org/t/namespace-editing-and-inputs-connected-to-ancestors/3049>\
**Category:** USD\
**Created:** [September 23, 2026, 2:43pm UTC](https://forum.aousd.org/t/namespace-editing-and-inputs-connected-to-ancestors/3049 "2026-09-23T14:43:07Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![James](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/james/32/168_2.png) [@James](https://forum.aousd.org/u/James)\
**Post date:** [September 23, 2026, 2:43pm UTC](https://forum.aousd.org/t/namespace-editing-and-inputs-connected-to-ancestors/3049/1 "2026-09-23T14:43:07Z")

</div>

Hey!

Currently hitting a problem I’m not 100% where the responsibility lies with regards to composition arcs, namespace editing and material encapsulation rules.

The scene overview: We have two materials. `Material0`, and `Material1`. `Material0` has a `NodeGraph` prim which has its properties “promoted” to its material interface (i.e its input property connects to its direct parent material input property). This matches proper encapsulation rules and is valid.  
Now on `Material1` we use a composition arc (doesn’t matter which at this point, I’ll use specialize), to specialize that `NodeGraph` into it. The internals of that NodeGraph, are handled correctly and appropriately by the Namespace editing (because it and its children fit within the encapsulation rules). However, the input on this inherited `NodeGraph` prim still connects to the original `Material0`. This feels counterintuitive. This actively breaks material encapsulation and causes all kinds of issues.  
Is this expected behaviour? I can see two other outcomes:

- NamespaceEditing should apply the same local editing to ensure it targets its new parent.
- Namespace editing remove connections which break direct encapsulation. Clearing the connection  
(Personally I prefer the clean removal/clearing of the `invalid` connection)

I’ve created a small example scene to hope to explain the problem

> **Example Flattened Layer**
>
> ```auto
> 
> def Cube "cube0" (
> prepend apiSchemas = ["MaterialBindingAPI"]
> )
> {
> rel material:binding = </materials/material0>
> double3 xformOp:translate:UsdCubeCreate = (-5, 0, 0)
> uniform token[] xformOpOrder = ["xformOp:translate:UsdCubeCreate"]
> }
> def Cube "cube1" (
> prepend apiSchemas = ["MaterialBindingAPI"]
> )
> {
> rel material:binding = </materials/material1>
> double3 xformOp:translate:UsdCubeCreate = (5, 0, 0)
> uniform token[] xformOpOrder = ["xformOp:translate:UsdCubeCreate"]
> }
> def "materials"
> {
> def Material "material0"
> {
> #red
> color3f inputs:diffuseColor = (1, 0, 0)
> token outputs:surface.connect = </materials/material0/nodegraph.outputs:surface>
> 
> def NodeGraph "nodegraph"
> {
> #cyan
> color3f inputs:diffuseColor = (0, 1, 1)
> color3f inputs:diffuseColor.connect = </materials/material0.inputs:diffuseColor>
> token outputs:surface.connect = </materials/material0/nodegraph/usdpreviewsurface.outputs:surface>
> 
> def Shader "usdpreviewsurface" ()
> {
> #green
> uniform token info:id = "UsdPreviewSurface"
> color3f inputs:diffuseColor = (0, 1, 0)
> color3f inputs:diffuseColor.connect = </materials/material0/nodegraph.inputs:diffuseColor>
> token outputs:surface
> }
> }
> }
> 
> def Material "material1"
> {
> #white
> color3f inputs:diffuseColor = (1.0, 1.0, 1.0)
> token outputs:surface.connect = </materials/material1/nodegraph1.outputs:surface>
> 
> def "nodegraph1" (
> prepend specializes = </materials/material0/nodegraph>
> )
> {
> #pink
> color3f inputs:diffuseColor = (1, 0, 1)
> token outputs:surface
> 
> over "usdpreviewsurface"
> {
> #blue
> color3f inputs:diffuseColor = (0, 0, 1)
> }
> }
> }
> }
> 
> ```

In this example I would expect `/cube0` to be Red (and it is). Then I’d expect `/cube1` to be either white or pink.  
White if the namespace editing rewires the connection to the new parent.  
Pink if the encapsulation breaking namespace edit is removed.

The actual result is that both are red, because the (although technically an invalid connection), the `Material1`, `NodeGraph` is still connected to the promoted value on `Material0`.

> **Result**
>
> ```auto
> #usda 1.0
> (
> doc = """Generated from Composed Stage of root layer 
> """
> )
> 
> def Cube "cube0" (
> apiSchemas = ["MaterialBindingAPI"]
> )
> {
> rel material:binding = </materials/material0>
> double3 xformOp:translate:UsdCubeCreate = (-5, 0, 0)
> uniform token[] xformOpOrder = ["xformOp:translate:UsdCubeCreate"]
> }
> def Cube "cube1" (
> apiSchemas = ["MaterialBindingAPI"]
> )
> {
> rel material:binding = </materials/material1>
> double3 xformOp:translate:UsdCubeCreate = (5, 0, 0)
> uniform token[] xformOpOrder = ["xformOp:translate:UsdCubeCreate"]
> }
> def "materials"
> {
> def Material "material0"
> {
> #red
> color3f inputs:diffuseColor = (1, 0, 0)
> token outputs:surface.connect = </materials/material0/nodegraph.outputs:surface>
> 
> def NodeGraph "nodegraph"
> {
> #cyan
> color3f inputs:diffuseColor = (0, 1, 1)
> color3f inputs:diffuseColor.connect = </materials/material0.inputs:diffuseColor>
> token outputs:surface.connect = </materials/material0/nodegraph/usdpreviewsurface.outputs:surface>
> 
> def Shader "usdpreviewsurface" ()
> {
> #green
> uniform token info:id = "UsdPreviewSurface"
> color3f inputs:diffuseColor = (0, 1, 0)
> color3f inputs:diffuseColor.connect = </materials/material0/nodegraph.inputs:diffuseColor>
> token outputs:surface
> }
> }
> }
> 
> def Material "material1"
> {
> #white
> color3f inputs:diffuseColor = (1, 1, 1)
> token outputs:surface.connect = </materials/material1/nodegraph1.outputs:surface>
> 
> def NodeGraph "nodegraph1"
> {
> #pink
> color3f inputs:diffuseColor = (1, 0, 1)
> color3f inputs:diffuseColor.connect = </materials/material0.inputs:diffuseColor>
> token outputs:surface.connect = </materials/material1/nodegraph1/usdpreviewsurface.outputs:surface>
> 
> def Shader "usdpreviewsurface" ()
> {
> uniform token info:id = "UsdPreviewSurface"
> color3f inputs:diffuseColor = (0, 0, 1)
> color3f inputs:diffuseColor.connect = </materials/material1/nodegraph1.inputs:diffuseColor>
> token outputs:surface
> }
> }
> }
> }
> 
> ```

Worth noting this is looking at things from a pure Material Encapsulation view of things, and doesn’t take account of namespace editing for connections not within Materials.

---

<div class="post-metadata">

**Author:** ![spiff](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/spiff/32/22_2.png) [@spiff](https://forum.aousd.org/u/spiff)\
**Post date:** [October 1, 2026, 4:16am UTC](https://forum.aousd.org/t/namespace-editing-and-inputs-connected-to-ancestors/3049/2 "2026-10-01T04:16:32Z")

</div>

Hi @James , I think I’m too steeped, because the behavior you’re seeing seems completely intuitive to me, for use of any composition arc 🙂 The **only** arc that does any “automatic remapping” of connection targets is **relocates** , but that remapping is only applied to the connections of **other prim attributes** that are targeting the relocated prim.

How is composition supposed to know that the targets of the prim attributes you’re referencing are semantically “targeting the properties of its **parent** prim”, as opposed to just the properties at the particular paths named in the connection targets? The safest, easiest, most-predictable thing we can do is preserve the connection targets (even though, in some circumstances, like a sub-root reference to another layerStack, that may cause the connections to break because the paths can’t be mapped in the referencing context). What would we do if there’s no clear relationship between the referenced prim and the targets of its connections `(/some/prim/in/a/namespace.attr.connect = </some/completely/different/path/in/namespace.bar>)` ?

We haven’t discussed hard rules for this kind of thing, but what you’re trying to do _feels_ like a violation of Material encapsulation… Given that you have already promoted NodeGraph properties to the Material interface via connections, I’m wondering why you need to specialize the NodeGraph rather than the whole Material?

To share a NodeGraph by itself, we’d typically define it elsewhere in namespace, and then reference it in to **both** Materials, establishing connections to Material interfaces uniquely in each. You _could_ keep doing what you’re doing but then manually reset the new NodeGraph’s connection targets to its Material’s same-named attributes.

Namespace Editing is a related, but separate concern. Everything I described about relocates should be true of namespace editing and copying, also, so namespace editing won’t “fix” those connection targets for you, either.

Let me know if this doesn’t make sense, please!

–spiff

---

<div class="post-metadata">

**Author:** ![James](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/james/32/168_2.png) [@James](https://forum.aousd.org/u/James)\
**Post date:** [October 7, 2026, 10:53am UTC](https://forum.aousd.org/t/namespace-editing-and-inputs-connected-to-ancestors/3049/3 "2026-10-07T10:53:25Z")

</div>

> To share a NodeGraph by itself, we’d typically define it elsewhere in namespace, and then reference it in to **both** Materials, establishing connections to Material interfaces uniquely in each. You _could_ keep doing what you’re doing but then manually reset the new NodeGraph’s connection targets to its Material’s same-named attributes.

Hi @spiff,

Thanks for the reply, and apologies for the delay, had typed it up but never hit enter! Your reasoning all makes sense and clarifies where the line is. I don’t disagree with the current workflow and chosen option of retaining the connection, and agree that in other cases it is the desired behaviour. I do have a question about:

> The **only** arc that does any “automatic remapping” of connection targets is **relocates** , but that remapping is only applied to the connections of **other prim attributes** that are targeting the relocated prim.

Since in the example the child prims of the specialized NodeGraph, `/material1/nodegraph1/usdpreviewSurface`, do have their connections remapped, since their absolute connections now target the new parent (`</materials/material1/nodegraph1.inputs:diffuseColor>`).

The example itself was relatively contrived, but the workflow would be to build a material up using a layered shader with additional building blocks. On our end it was about possibly shortcutting that creation of the NodeGraph outside of the material, but in hindsight, I believe that your suggestion is likely the better workflow we should be pointing users towards.

Thanks again,  
James

---

<div class="post-metadata">

**Author:** ![spiff](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/spiff/32/22_2.png) [@spiff](https://forum.aousd.org/u/spiff)\
**Post date:** [October 7, 2026, 11:09pm UTC](https://forum.aousd.org/t/namespace-editing-and-inputs-connected-to-ancestors/3049/4 "2026-10-07T23:09:07Z")

</div>

> [@James](#):
>
> Since in the example the child prims of the specialized NodeGraph, `/material1/nodegraph1/usdpreviewSurface`, do have their connections remapped, since their absolute connections now target the new parent (`</materials/material1/nodegraph1.inputs:diffuseColor>`).

Good point - I stand corrected! That’s a good illustration of what encapsulation means and why it’s important. Those child connections _are_ encapsulated inside the reference root, so it’s clear that they should be remapped when the root itself is referenced (and must be, since referencing is a path-prefixing operation), and why resolving a relationship/connection authored inside a referenced tree that points outside the tree will generate a “I can’t map this path” error… unless it’s an internal reference. Composition isn’t subtle at all.
