This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git

commit 21f5389e0e2242eaea1e17000a0d8e838cf4e153
Author: James Bognar <[email protected]>
AuthorDate: Fri Jul 24 16:52:44 2026 -0400

    Add @Child annotation for host-side seeding of child REST contexts 
(TODO-182)
    
    Adds @Child and @Rest(childrenDefs=...) mirroring @Mixin/mixinDefs, letting 
a
    host seed a curated set of settings onto a routed child's otherwise-isolated
    RestContext. Additive-security attributes (guards, converters, roleGuard,
    rolesDeclared) prepend/AND-stack; child-wins scalars (callLogger,
    partSerializer, partParser, debug, defaultCharset, maxInput) act as 
fallbacks.
    Seeds never override a child's own explicit config; a child's noInherit cuts
    the seed. Includes lazy-children seed threading and a fix to
    computeRawRestAnnotations() DefaultConfig partitioning. Also includes 7
    incidental java:S2259 false-positive suppressions in RestContext.java.
    
    Co-authored-by: Cursor <[email protected]>
---
 .../juneau/rest/child/ChildContext_Seed_Test.java  | 147 +++++++++++
 .../rest/child/ChildDefs_Discovery_Test.java       |  70 +++++
 .../rest/child/ChildDefs_Equivalence_Test.java     |  56 ++++
 .../child/ChildInheritance_Converters_Test.java    |  84 ++++++
 .../rest/child/ChildInheritance_Debug_Test.java    | 105 ++++++++
 .../ChildInheritance_DefaultConfigChild_Test.java  | 110 ++++++++
 .../rest/child/ChildInheritance_Guards_Test.java   | 124 +++++++++
 .../child/ChildInheritance_NoInherit_Test.java     |  95 +++++++
 .../child/ChildInheritance_PartParser_Test.java    |  88 +++++++
 .../ChildInheritance_PartSerializer_Test.java      |  88 +++++++
 .../child/ChildInheritance_RoleGuard_Test.java     |  95 +++++++
 .../rest/child/ChildInheritance_Scalars_Test.java  | 114 ++++++++
 .../juneau/rest/server/ChildAnnotation_Test.java   | 118 +++++++++
 .../rest/server/LazyChildren_ChildDefs_Test.java   |  78 ++++++
 .../java/org/apache/juneau/rest/server/Child.java  | 165 ++++++++++++
 .../apache/juneau/rest/server/ChildAnnotation.java | 225 ++++++++++++++++
 .../java/org/apache/juneau/rest/server/Rest.java   |  41 +++
 .../apache/juneau/rest/server/RestAnnotation.java  |  19 ++
 .../apache/juneau/rest/server/RestChildren.java    |  48 +++-
 .../org/apache/juneau/rest/server/RestContext.java | 290 ++++++++++++++++++---
 .../juneau/rest/server/RestServerConstants.java    |   3 +
 21 files changed, 2120 insertions(+), 43 deletions(-)

diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildContext_Seed_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildContext_Seed_Test.java
new file mode 100644
index 0000000000..5e51695ecb
--- /dev/null
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildContext_Seed_Test.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.child;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.commons.inject.*;
+import org.apache.juneau.rest.mock.classic.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.guard.*;
+import org.apache.juneau.rest.server.logger.*;
+import org.apache.juneau.rest.server.servlet.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Phase 2 canary &mdash; proves the {@code @Child} seed-injection mechanism 
end-to-end using {@code callLogger}
+ * (a single child-wins scalar) as a witness, mirroring {@code 
MixinInheritance_CallLogger_Test}.
+ *
+ * <p>
+ * {@code callLogger} is chosen because {@link RestContext#getCallLogger()} is 
a direct, public, class-level
+ * getter with no request/op machinery needed to observe it.
+ */
+class ChildContext_Seed_Test extends TestBase {
+
+       public static class SeedLogger extends CallLogger {
+               public SeedLogger(BeanStore bs) { super(bs); }
+       }
+
+       public static class ChildLogger extends CallLogger {
+               public ChildLogger(BeanStore bs) { super(bs); }
+       }
+
+       public static class BlockAllGuard extends RestGuard {
+               @Override public boolean isRequestAllowed(RestRequest req) { 
return false; }
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a01: child declares no callLogger -> host's @Child(callLogger=...) 
seed takes effect.
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/nologger")
+       public static class ChildNoLoggerDeclared {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildNoLoggerDeclared.class, 
callLogger=SeedLogger.class))
+       public static class HostSeedsLogger extends BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a01_childWithNoDeclarationReceivesSeed() {
+               MockRestClient.buildLax(HostSeedsLogger.class);
+               var hostCtx = 
RestContext.getGlobalRegistry().get(HostSeedsLogger.class);
+               var childCtx = 
hostCtx.getRestChildren().asMap().get("nologger");
+               assertNotNull(childCtx);
+               assertInstanceOf(SeedLogger.class, childCtx.getCallLogger(),
+                       "Child with no callLogger declaration must receive the 
host's seeded SeedLogger");
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a02: child's own explicit callLogger declaration wins over the 
host's seed.
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/ownlogger", callLogger=ChildLogger.class)
+       public static class ChildWithOwnLogger {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildWithOwnLogger.class, 
callLogger=SeedLogger.class))
+       public static class HostSeedsLoggerButChildWins extends 
BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a02_childsOwnDeclarationWins() {
+               MockRestClient.buildLax(HostSeedsLoggerButChildWins.class);
+               var hostCtx = 
RestContext.getGlobalRegistry().get(HostSeedsLoggerButChildWins.class);
+               var childCtx = 
hostCtx.getRestChildren().asMap().get("ownlogger");
+               assertNotNull(childCtx);
+               assertInstanceOf(ChildLogger.class, childCtx.getCallLogger(),
+                       "Child's own explicit callLogger must win over the 
host's seeded SeedLogger");
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a03: isolation preserved -- host's OWN (non-seeded) 
@Rest(guards=...) must not leak to the child, even
+       // though the child receives a callLogger seed.  If guards leaked, the 
child endpoint would 403.
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/isocheck")
+       public static class ChildForIsolation {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(guards=BlockAllGuard.class, 
childrenDefs=@Child(type=ChildForIsolation.class, callLogger=SeedLogger.class))
+       public static class HostWithGuardsAndSeed extends BasicRestServletGroup 
{
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a03_isolationPreserved() throws Exception {
+               var c = MockRestClient.buildLax(HostWithGuardsAndSeed.class);
+               // Host's own (non-seeded) guards must not leak into the 
child's isolated context.
+               
c.get("/isocheck/me").accept("text/plain").run().assertStatus(200).assertContent("me");
+               var hostCtx = 
RestContext.getGlobalRegistry().get(HostWithGuardsAndSeed.class);
+               var childCtx = 
hostCtx.getRestChildren().asMap().get("isocheck");
+               assertInstanceOf(SeedLogger.class, childCtx.getCallLogger(),
+                       "The seeded callLogger must still apply alongside 
isolation from the host's own guards");
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a04: the seed is not masked by the synthesized DefaultConfig 
fallback -- DefaultConfig sets
+       // maxInput="1000000"; a host-seeded @Child(maxInput=...) must still 
win (seed ranks above DefaultConfig).
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/maxcheck")
+       public static class ChildForMaxInput {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildForMaxInput.class, 
maxInput="12345"))
+       public static class HostSeedsMaxInput extends BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a04_seedNotMaskedByDefaultConfigFallback() {
+               MockRestClient.buildLax(HostSeedsMaxInput.class);
+               var hostCtx = 
RestContext.getGlobalRegistry().get(HostSeedsMaxInput.class);
+               var childCtx = 
hostCtx.getRestChildren().asMap().get("maxcheck");
+               assertNotNull(childCtx);
+               var opCtx = childCtx.getRestOperations().getOpContexts().get(0);
+               assertEquals(12345L, opCtx.getMaxInput(),
+                       "Host-seeded maxInput must win over the framework's 
DefaultConfig fallback (1000000)");
+       }
+}
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildDefs_Discovery_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildDefs_Discovery_Test.java
new file mode 100644
index 0000000000..f960dd77bb
--- /dev/null
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildDefs_Discovery_Test.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.child;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.rest.mock.classic.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.servlet.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Discovery-order and dedup tests for {@link Rest#childrenDefs() 
@Rest(childrenDefs=@Child(...))}, mirroring
+ * {@code MixinDefs_Overrides_Test} sections F/I/M for the {@code 
mixins}/{@code mixinDefs} discovery rules.
+ */
+class ChildDefs_Discovery_Test extends TestBase {
+
+       @Rest(path="/plain")
+       public static class PlainChild {
+               @RestGet("/ping") public String ping() { return "pong"; }
+       }
+
+       @Rest(path="/plain2")
+       public static class PlainChild2 {
+               @RestGet("/ping") public String ping() { return "pong2"; }
+       }
+
+       // A class named in both children= and childrenDefs= must be mounted 
exactly once.
+       @Rest(children=PlainChild.class, 
childrenDefs=@Child(type=PlainChild.class))
+       public static class DedupHost extends BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a01_dedupedToFirstOccurrence() throws Exception {
+               var c = MockRestClient.buildLax(DedupHost.class);
+               
c.get("/plain/ping").accept("text/plain").run().assertStatus(200).assertContent("pong");
+               var hostCtx = 
RestContext.getGlobalRegistry().get(DedupHost.class);
+               assertEquals(1, hostCtx.getRestChildren().asMap().size());
+       }
+
+       // Bare children= entries are discovered before childrenDefs= entries.
+       @Rest(children=PlainChild.class, 
childrenDefs=@Child(type=PlainChild2.class))
+       public static class OrderHost extends BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a02_bareDiscoveredBeforeChildrenDefs() {
+               MockRestClient.buildLax(OrderHost.class);
+               var hostCtx = 
RestContext.getGlobalRegistry().get(OrderHost.class);
+               var keys = new 
ArrayList<>(hostCtx.getRestChildren().asMap().keySet());
+               assertEquals(List.of("plain", "plain2"), keys);
+       }
+}
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildDefs_Equivalence_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildDefs_Equivalence_Test.java
new file mode 100644
index 0000000000..2f950cdbb6
--- /dev/null
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildDefs_Equivalence_Test.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.child;
+
+import org.apache.juneau.*;
+import org.apache.juneau.rest.mock.classic.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.servlet.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Feature test for the {@link Rest#childrenDefs() 
@Rest(childrenDefs=@Child(...))} bare-equivalence rule.
+ *
+ * <p>
+ * Verifies the Phase 1 motivating capability &mdash; {@code @Child(type=X)} 
with zero overrides routes
+ * identically to a bare {@code @Rest(children=X)} entry &mdash; mirroring
+ * {@code MixinDefs_Overrides_Test.f01_bareEqualsEmptyDef}.
+ */
+class ChildDefs_Equivalence_Test extends TestBase {
+
+       @Rest(path="/plain")
+       public static class PlainChild {
+               @RestGet("/ping") public String ping() { return "pong"; }
+       }
+
+       @Rest(children=PlainChild.class)
+       public static class Bare extends BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Rest(childrenDefs=@Child(type=PlainChild.class))
+       public static class EmptyDef extends BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a01_bareEqualsEmptyChildDef() throws Exception {
+               var bare = MockRestClient.buildLax(Bare.class);
+               var def = MockRestClient.buildLax(EmptyDef.class);
+               
bare.get("/plain/ping").accept("text/plain").run().assertStatus(200).assertContent("pong");
+               
def.get("/plain/ping").accept("text/plain").run().assertStatus(200).assertContent("pong");
+       }
+}
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_Converters_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_Converters_Test.java
new file mode 100644
index 0000000000..831e0006e8
--- /dev/null
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_Converters_Test.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.child;
+
+import org.apache.juneau.*;
+import org.apache.juneau.http.response.*;
+import org.apache.juneau.marshall.serializer.*;
+import org.apache.juneau.rest.mock.classic.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.converter.*;
+import org.apache.juneau.rest.server.servlet.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Phase 3 &mdash; proves the additive-security PREPEND shape for {@code 
@Child(converters=...)}: the
+ * host-seeded converter applies when the child declares none of its own, and 
runs BEFORE the child's own
+ * declared converter when both are present (prepend order, verified via 
string-append ordering).
+ */
+class ChildInheritance_Converters_Test extends TestBase {
+
+       public static class SeedConv implements RestConverter {
+               @Override public Object convert(RestRequest req, Object res) 
throws BasicHttpException, SerializeException {
+                       return res + "+seed";
+               }
+       }
+
+       public static class ChildConv implements RestConverter {
+               @Override public Object convert(RestRequest req, Object res) 
throws BasicHttpException, SerializeException {
+                       return res + "+child";
+               }
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a01: child with no converters of its own -> the seeded converter 
applies.
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/noconv")
+       public static class ChildNoConv {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildNoConv.class, 
converters=SeedConv.class))
+       public static class HostSeedsConvOnly extends BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a01_seededConverterAppliesWhenChildHasNone() throws 
Exception {
+               var c = MockRestClient.buildLax(HostSeedsConvOnly.class);
+               
c.get("/noconv/me").accept("text/plain").run().assertStatus(200).assertContent("me+seed");
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a02: child declares its own converter too -> BOTH apply, seed's 
converter runs first (prepend order).
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/bothconv", converters=ChildConv.class)
+       public static class ChildWithOwnConv {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildWithOwnConv.class, 
converters=SeedConv.class))
+       public static class HostSeedsConvOrder extends BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a02_seedRunsBeforeChildsOwnConverter() throws Exception {
+               var c = MockRestClient.buildLax(HostSeedsConvOrder.class);
+               
c.get("/bothconv/me").accept("text/plain").run().assertStatus(200).assertContent("me+seed+child");
+       }
+}
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_Debug_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_Debug_Test.java
new file mode 100644
index 0000000000..94dd2ac311
--- /dev/null
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_Debug_Test.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.child;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.logging.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.rest.mock.MockServletRequest;
+import org.apache.juneau.rest.mock.classic.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.debug.*;
+import org.apache.juneau.rest.server.servlet.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Phase 4 &mdash; proves that {@code @Child(debug=...)} seeds each of {@code 
@Debug}'s three sub-fields
+ * (value/format/level) INDEPENDENTLY (per {@code RestContext.debugConfig}), 
not as one atomic all-or-nothing
+ * scalar: a child declaring only one sub-field still receives the seed's 
value for the others.
+ */
+class ChildInheritance_Debug_Test extends TestBase {
+
+       public static class SeedFormat implements DebugFormat {
+               @Override public String format(DebugFormatContext context) { 
return "seed-format"; }
+       }
+
+       public static class ChildFormat implements DebugFormat {
+               @Override public String format(DebugFormatContext context) { 
return "child-format"; }
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a01: host seeds value="always" + format=SeedFormat; child declares 
ONLY its own level="FINE".
+       // Expected: child's own level wins (independent sub-field); the seed's 
value/format still apply because
+       // the child left them at their sentinel.
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/subfields", debug=@Debug(level="FINE"))
+       public static class ChildWithOnlyLevel {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildWithOnlyLevel.class, 
debug=@Debug(value="always", format=SeedFormat.class)))
+       public static class HostSeedsValueAndFormat extends 
BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a01_subFieldsIndependentlySeeded() {
+               MockRestClient.buildLax(HostSeedsValueAndFormat.class);
+               var hostCtx = 
RestContext.getGlobalRegistry().get(HostSeedsValueAndFormat.class);
+               var childCtx = 
hostCtx.getRestChildren().asMap().get("subfields");
+               assertNotNull(childCtx);
+
+               var debugConfig = 
childCtx.getBeanStore().getBean(DebugConfig.class).orElse(null);
+               assertNotNull(debugConfig, "Child context must expose a 
resolved DebugConfig bean");
+
+               var result = debugConfig.resolve(childCtx, 
MockServletRequest.create());
+               assertTrue(result.enabled(), "Seed's value=\"always\" must 
enable debug even though the child never declared its own value");
+               assertInstanceOf(SeedFormat.class, result.format(), "Seed's 
format must apply since the child left format at its sentinel");
+               assertEquals(Level.FINE, result.level(), "Child's own explicit 
level must win over any (absent) seed level");
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a02: child declares its own value="never" (opting out); the seed's 
format still applies because the child
+       // left format at its sentinel -- proving independence runs both ways.
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/subfields2", debug=@Debug(value="never"))
+       public static class ChildWithOnlyValue {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildWithOnlyValue.class, 
debug=@Debug(value="always", format=SeedFormat.class)))
+       public static class HostSeedsValueButChildOptsOut extends 
BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a02_childsOwnSubFieldWinsOthersInherited() {
+               MockRestClient.buildLax(HostSeedsValueButChildOptsOut.class);
+               var hostCtx = 
RestContext.getGlobalRegistry().get(HostSeedsValueButChildOptsOut.class);
+               var childCtx = 
hostCtx.getRestChildren().asMap().get("subfields2");
+               assertNotNull(childCtx);
+
+               var debugConfig = 
childCtx.getBeanStore().getBean(DebugConfig.class).orElse(null);
+               assertNotNull(debugConfig);
+
+               var result = debugConfig.resolve(childCtx, 
MockServletRequest.create());
+               assertFalse(result.enabled(), "Child's own explicit 
value=\"never\" must win over the seed's value=\"always\"");
+               assertInstanceOf(SeedFormat.class, result.format(), "Seed's 
format must still apply -- the child left format at its sentinel");
+       }
+}
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_DefaultConfigChild_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_DefaultConfigChild_Test.java
new file mode 100644
index 0000000000..447a265e98
--- /dev/null
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_DefaultConfigChild_Test.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.child;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.rest.mock.classic.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.servlet.*;
+import org.apache.juneau.utest.utils.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Phase 4 hardening &mdash; regression test for an edge case not called out 
explicitly by name in spec &sect;6.6:
+ * a CHILD CLASS that itself transitively implements {@code DefaultConfig} 
(e.g. by extending
+ * {@code BasicRestServlet}/{@code BasicRestServletGroup}, which implement 
{@code BasicUniversalConfig extends
+ * DefaultConfig}), rather than being a plain POJO with no framework-default 
mixin.
+ *
+ * <p>
+ * <b>Bug found and fixed by this test (Phase 4 hardening):</b> {@code 
computeRawRestAnnotations()}'s
+ * {@code hasDefaultConfig}-true branch originally appended the seed 
annotation after the ENTIRE {@code raw}
+ * chain unconditionally. For a plain child class (the common case exercised 
by every other Phase 2-4 test),
+ * {@code raw} never embeds {@code DefaultConfig} itself, so appending after 
{@code raw} correctly placed the
+ * seed above the framework's separately-synthesized {@code DefaultConfig} 
fallback. But when the child class
+ * itself transitively implements {@code DefaultConfig}, that interface's own 
{@code @Rest} entry is already
+ * PART OF {@code raw} (typically at its most-ancestor/least-derived slot) — 
appending the seed after the
+ * <i>entire</i> {@code raw} list placed the seed BELOW that embedded {@code 
DefaultConfig} entry, so
+ * {@code DefaultConfig}'s own concrete defaults (e.g. {@code 
partSerializer=OpenApiSerializer.class}) masked
+ * the host's seed entirely, violating spec &sect;6.6's invariant that the 
seed ranks above the {@code
+ * DefaultConfig} fallback. Fixed by splitting {@code raw} into "the child's 
own real declarations" and "the
+ * (embedded) DefaultConfig entries" and inserting the seed between them, 
mirroring exactly how the
+ * separately-synthesized case already worked.
+ */
+class ChildInheritance_DefaultConfigChild_Test extends TestBase {
+
+       public static class SeedPS extends FakeWriterSerializer {
+               public SeedPS(FakeWriterSerializer.Builder b) { super(b); }
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a01: child class extends BasicRestServlet (transitively implements 
DefaultConfig) and declares no
+       // partSerializer of its own -> the host's seed must still win over 
DefaultConfig's own baked-in
+       // partSerializer=OpenApiSerializer.class default, not be masked by it.
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/dcchild")
+       public static class ChildImplementingDefaultConfig extends 
BasicRestServlet {
+               private static final long serialVersionUID = 1L;
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildImplementingDefaultConfig.class, 
partSerializer=SeedPS.class))
+       public static class HostSeedsIntoDefaultConfigChild extends 
BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a01_seedNotMaskedByChildsOwnEmbeddedDefaultConfig() {
+               MockRestClient.buildLax(HostSeedsIntoDefaultConfigChild.class);
+               var hostCtx = 
RestContext.getGlobalRegistry().get(HostSeedsIntoDefaultConfigChild.class);
+               var childCtx = hostCtx.getRestChildren().asMap().get("dcchild");
+               assertNotNull(childCtx);
+               assertEquals(SeedPS.class, 
childCtx.getPartSerializer().getClass(),
+                       "Host-seeded partSerializer must win over 
DefaultConfig's own baked-in OpenApiSerializer default, "
+                       + "even when the child class transitively implements 
DefaultConfig itself");
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a02: same shape, but the child ALSO declares its own explicit 
partSerializer -> the child's own
+       // declaration must still win over both the seed AND DefaultConfig 
(three-way precedence check).
+       
//------------------------------------------------------------------------------------------------------------
+
+       public static class ChildPS extends FakeWriterSerializer {
+               public ChildPS(FakeWriterSerializer.Builder b) { super(b); }
+       }
+
+       @Rest(path="/dcchild2", partSerializer=ChildPS.class)
+       public static class ChildImplementingDefaultConfigWithOwnPS extends 
BasicRestServlet {
+               private static final long serialVersionUID = 1L;
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       
@Rest(childrenDefs=@Child(type=ChildImplementingDefaultConfigWithOwnPS.class, 
partSerializer=SeedPS.class))
+       public static class HostSeedsIntoDefaultConfigChildWithOwnPS extends 
BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a02_childsOwnDeclarationStillWinsOverSeedAndDefaultConfig() {
+               
MockRestClient.buildLax(HostSeedsIntoDefaultConfigChildWithOwnPS.class);
+               var hostCtx = 
RestContext.getGlobalRegistry().get(HostSeedsIntoDefaultConfigChildWithOwnPS.class);
+               var childCtx = 
hostCtx.getRestChildren().asMap().get("dcchild2");
+               assertNotNull(childCtx);
+               assertEquals(ChildPS.class, 
childCtx.getPartSerializer().getClass(),
+                       "Child's own explicit partSerializer must win over both 
the host's seed and DefaultConfig's default");
+       }
+}
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_Guards_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_Guards_Test.java
new file mode 100644
index 0000000000..0f6924bc2e
--- /dev/null
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_Guards_Test.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.child;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.*;
+import java.util.concurrent.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.rest.mock.classic.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.guard.*;
+import org.apache.juneau.rest.server.servlet.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Phase 3 &mdash; proves the additive-security PREPEND shape for {@code 
@Child(guards=...)}: the host-seeded
+ * guard applies when the child declares none of its own (degrades to a plain 
"set"), the seeded guard runs
+ * outermost (first) when the child also declares its own guard, and the 
child's own guard is never dropped.
+ */
+class ChildInheritance_Guards_Test extends TestBase {
+
+       static final List<String> ORDER = new CopyOnWriteArrayList<>();
+       private static final List<String> EXPECTED_ORDER = List.of("seed", 
"child");
+
+       public static class SeedGuard extends RestGuard {
+               @Override public boolean isRequestAllowed(RestRequest req) { 
ORDER.add("seed"); return true; }
+       }
+
+       public static class ChildGuard extends RestGuard {
+               @Override public boolean isRequestAllowed(RestRequest req) { 
ORDER.add("child"); return true; }
+       }
+
+       public static class BlockingSeedGuard extends RestGuard {
+               @Override public boolean isRequestAllowed(RestRequest req) {
+                       return 
"yes".equals(req.getHeaderParam("X-Seed-Allowed").orElse(null));
+               }
+       }
+
+       public static class AllowOnlyChildHeader extends RestGuard {
+               @Override public boolean isRequestAllowed(RestRequest req) {
+                       return 
"yes".equals(req.getHeaderParam("X-Child-Allowed").orElse(null));
+               }
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a01: child with no guards of its own -> the seeded guard applies 
(degrades to a plain "set").
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/noguard")
+       public static class ChildNoGuard {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildNoGuard.class, 
guards=BlockingSeedGuard.class))
+       public static class HostSeedsGuardOnly extends BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a01_seededGuardAppliesWhenChildHasNone() throws Exception {
+               var c = MockRestClient.buildLax(HostSeedsGuardOnly.class);
+               c.get("/noguard/me").run().assertStatus(403);
+               c.get("/noguard/me").header("X-Seed-Allowed", 
"yes").accept("text/plain").run().assertStatus(200).assertContent("me");
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a02: child declares its own guard too -> BOTH apply, and the seed's 
guard runs first (outermost).
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/bothguards", guards=ChildGuard.class)
+       public static class ChildWithOwnGuard {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildWithOwnGuard.class, 
guards=SeedGuard.class))
+       public static class HostSeedsGuardOrder extends BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a02_bothApplySeedRunsFirst() throws Exception {
+               ORDER.clear();
+               var c = MockRestClient.buildLax(HostSeedsGuardOrder.class);
+               
c.get("/bothguards/me").accept("text/plain").run().assertStatus(200).assertContent("me");
+               assertEquals(EXPECTED_ORDER, ORDER,
+                       "Host-seeded guard must run outermost (first), before 
the child's own declared guard");
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a03: the child's own guard is never dropped -- the seed's guard 
alone would allow the request, but the
+       // child's own (stricter) guard must still gate it.
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/dropcheck", guards=AllowOnlyChildHeader.class)
+       public static class ChildWithOwnGuard2 {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildWithOwnGuard2.class, 
guards=SeedGuard.class))
+       public static class HostSeedsGuardKeepsChildGuard extends 
BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a03_childsOwnGuardNeverDropped() throws Exception {
+               var c = 
MockRestClient.buildLax(HostSeedsGuardKeepsChildGuard.class);
+               // SeedGuard always allows, but the child's own 
AllowOnlyChildHeader guard must still gate the request.
+               c.get("/dropcheck/me").run().assertStatus(403);
+               c.get("/dropcheck/me").header("X-Child-Allowed", 
"yes").accept("text/plain").run().assertStatus(200).assertContent("me");
+       }
+}
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_NoInherit_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_NoInherit_Test.java
new file mode 100644
index 0000000000..3a5e42d6c5
--- /dev/null
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_NoInherit_Test.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.child;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.rest.mock.classic.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.guard.*;
+import org.apache.juneau.rest.server.logger.*;
+import org.apache.juneau.rest.server.servlet.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Phase 5 &mdash; proves that a child's own {@code 
@Rest(noInherit="&lt;property&gt;")} cuts the corresponding
+ * {@code @Child} seed too, per spec &sect;4's "{@code noInherit} interaction" 
subsection: because the seed is
+ * injected as an ordinary least-derived entry in the child's own {@code 
getRestAnnotations()} chain
+ * (&sect;6.6), the existing generic {@code noInherit} cutoff scan in {@code 
getRestAnnotationsForProperty}
+ * naturally truncates it too, with no seed-aware special-casing required. 
Verified uniformly across BOTH
+ * seedable-property buckets: an additive-security member ({@code guards}) and 
a child-wins scalar
+ * ({@code callLogger}).
+ */
+class ChildInheritance_NoInherit_Test extends TestBase {
+
+       public static class BlockAllGuard extends RestGuard {
+               @Override public boolean isRequestAllowed(RestRequest req) { 
return false; }
+       }
+
+       public static class SeedLogger extends CallLogger {
+               public SeedLogger(org.apache.juneau.commons.inject.BeanStore 
bs) { super(bs); }
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a01: additive-security member (guards) -- child declares 
noInherit="guards" -> receives NONE of the
+       // host's seeded guards (the endpoint must be reachable WITHOUT 
satisfying the seeded guard at all, not
+       // merely "the child's own guards are present").
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/noinheritguards", noInherit="guards")
+       public static class ChildNoInheritGuards {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildNoInheritGuards.class, 
guards=BlockAllGuard.class))
+       public static class HostSeedsGuardButChildOptsOut extends 
BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a01_noInheritCutsSeededGuards() throws Exception {
+               var c = 
MockRestClient.buildLax(HostSeedsGuardButChildOptsOut.class);
+               // If the seeded BlockAllGuard were still applied, this request 
would always 403. noInherit="guards"
+               // must cut it entirely, leaving the child with zero guards.
+               
c.get("/noinheritguards/me").accept("text/plain").run().assertStatus(200).assertContent("me");
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a02: child-wins scalar (callLogger) -- child declares 
noInherit="callLogger" -> receives NONE of the
+       // host's seeded callLogger (falls back to the framework default, not 
the seed).
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/noinheritlogger", noInherit="callLogger")
+       public static class ChildNoInheritLogger {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildNoInheritLogger.class, 
callLogger=SeedLogger.class))
+       public static class HostSeedsLoggerButChildOptsOut extends 
BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a02_noInheritCutsSeededCallLogger() {
+               MockRestClient.buildLax(HostSeedsLoggerButChildOptsOut.class);
+               var hostCtx = 
RestContext.getGlobalRegistry().get(HostSeedsLoggerButChildOptsOut.class);
+               var childCtx = 
hostCtx.getRestChildren().asMap().get("noinheritlogger");
+               assertNotNull(childCtx);
+               assertFalse(childCtx.getCallLogger() instanceof SeedLogger,
+                       "Child's own noInherit=\"callLogger\" must cut the 
host's seeded SeedLogger entirely, "
+                       + "falling back to the framework default rather than 
the seed");
+       }
+}
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_PartParser_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_PartParser_Test.java
new file mode 100644
index 0000000000..28f9addd49
--- /dev/null
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_PartParser_Test.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.child;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.rest.mock.classic.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.servlet.*;
+import org.apache.juneau.utest.utils.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Phase 4 &mdash; proves the CHILD-WINS scalar shape for {@code 
@Child(partParser=...)}: the host-seeded
+ * part parser applies when the child declares none of its own, but the 
child's own explicit
+ * {@code @Rest(partParser=...)} wins over the seed when present.
+ */
+class ChildInheritance_PartParser_Test extends TestBase {
+
+       public static class SeedPP extends FakeReaderParser {
+               public SeedPP(FakeReaderParser.Builder b) { super(b); }
+       }
+
+       public static class ChildPP extends FakeReaderParser {
+               public ChildPP(FakeReaderParser.Builder b) { super(b); }
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a01: child declares no partParser -> host's seed takes effect.
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/nopp")
+       public static class ChildNoPPDeclared {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildNoPPDeclared.class, 
partParser=SeedPP.class))
+       public static class HostSeedsPP extends BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a01_childWithNoDeclarationReceivesSeed() {
+               MockRestClient.buildLax(HostSeedsPP.class);
+               var hostCtx = 
RestContext.getGlobalRegistry().get(HostSeedsPP.class);
+               var childCtx = hostCtx.getRestChildren().asMap().get("nopp");
+               assertNotNull(childCtx);
+               assertEquals(SeedPP.class, childCtx.getPartParser().getClass(),
+                       "Child with no partParser declaration must receive the 
host's seeded SeedPP");
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a02: child's own explicit partParser wins over the host's seed.
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/ownpp", partParser=ChildPP.class)
+       public static class ChildWithOwnPP {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildWithOwnPP.class, 
partParser=SeedPP.class))
+       public static class HostSeedsPPButChildWins extends 
BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a02_childsOwnDeclarationWins() {
+               MockRestClient.buildLax(HostSeedsPPButChildWins.class);
+               var hostCtx = 
RestContext.getGlobalRegistry().get(HostSeedsPPButChildWins.class);
+               var childCtx = hostCtx.getRestChildren().asMap().get("ownpp");
+               assertNotNull(childCtx);
+               assertEquals(ChildPP.class, childCtx.getPartParser().getClass(),
+                       "Child's own explicit partParser must win over the 
host's seeded SeedPP");
+       }
+}
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_PartSerializer_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_PartSerializer_Test.java
new file mode 100644
index 0000000000..4a01122046
--- /dev/null
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_PartSerializer_Test.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.child;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.rest.mock.classic.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.servlet.*;
+import org.apache.juneau.utest.utils.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Phase 4 &mdash; proves the CHILD-WINS scalar shape for {@code 
@Child(partSerializer=...)}: the host-seeded
+ * part serializer applies when the child declares none of its own, but the 
child's own explicit
+ * {@code @Rest(partSerializer=...)} wins over the seed when present.
+ */
+class ChildInheritance_PartSerializer_Test extends TestBase {
+
+       public static class SeedPS extends FakeWriterSerializer {
+               public SeedPS(FakeWriterSerializer.Builder b) { super(b); }
+       }
+
+       public static class ChildPS extends FakeWriterSerializer {
+               public ChildPS(FakeWriterSerializer.Builder b) { super(b); }
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a01: child declares no partSerializer -> host's seed takes effect.
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/nops")
+       public static class ChildNoPSDeclared {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildNoPSDeclared.class, 
partSerializer=SeedPS.class))
+       public static class HostSeedsPS extends BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a01_childWithNoDeclarationReceivesSeed() {
+               MockRestClient.buildLax(HostSeedsPS.class);
+               var hostCtx = 
RestContext.getGlobalRegistry().get(HostSeedsPS.class);
+               var childCtx = hostCtx.getRestChildren().asMap().get("nops");
+               assertNotNull(childCtx);
+               assertEquals(SeedPS.class, 
childCtx.getPartSerializer().getClass(),
+                       "Child with no partSerializer declaration must receive 
the host's seeded SeedPS");
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a02: child's own explicit partSerializer wins over the host's seed.
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/ownps", partSerializer=ChildPS.class)
+       public static class ChildWithOwnPS {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildWithOwnPS.class, 
partSerializer=SeedPS.class))
+       public static class HostSeedsPSButChildWins extends 
BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a02_childsOwnDeclarationWins() {
+               MockRestClient.buildLax(HostSeedsPSButChildWins.class);
+               var hostCtx = 
RestContext.getGlobalRegistry().get(HostSeedsPSButChildWins.class);
+               var childCtx = hostCtx.getRestChildren().asMap().get("ownps");
+               assertNotNull(childCtx);
+               assertEquals(ChildPS.class, 
childCtx.getPartSerializer().getClass(),
+                       "Child's own explicit partSerializer must win over the 
host's seeded SeedPS");
+       }
+}
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_RoleGuard_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_RoleGuard_Test.java
new file mode 100644
index 0000000000..25b93ac137
--- /dev/null
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_RoleGuard_Test.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.child;
+
+import org.apache.juneau.*;
+import org.apache.juneau.rest.mock.classic.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.servlet.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Phase 3 &mdash; proves the additive-security AND-STACK shape for {@code 
@Child(roleGuard=...)}: a
+ * host-seeded {@code roleGuard} and the child's own explicit {@code 
roleGuard} both apply as independent,
+ * ANDed {@code RoleBasedRestGuard} instances &mdash; the child can add 
restriction but never remove or
+ * weaken the host's.
+ */
+class ChildInheritance_RoleGuard_Test extends TestBase {
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a01: host seeds roleGuard="admin"; child declares its own 
roleGuard="special" -> BOTH must be satisfied.
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/rg", roleGuard="special")
+       public static class ChildWithOwnRoleGuard {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildWithOwnRoleGuard.class, 
roleGuard="admin"))
+       public static class HostSeedsRoleGuard extends BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a01_bothRoleGuardsMustBeSatisfied() throws Exception {
+               var c = MockRestClient.buildLax(HostSeedsRoleGuard.class);
+               c.get("/rg/me").roles("admin").run().assertStatus(403);         
     // missing "special"
+               c.get("/rg/me").roles("special").run().assertStatus(403);       
     // missing "admin"
+               c.get("/rg/me").roles("admin", 
"special").accept("text/plain").run().assertStatus(200).assertContent("me");
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a02: child declares no roleGuard of its own -> only the host-seeded 
roleGuard applies.
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/rg2")
+       public static class ChildNoRoleGuard {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildNoRoleGuard.class, 
roleGuard="admin"))
+       public static class HostSeedsRoleGuardOnly extends 
BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a02_seedOnlyAppliesWhenChildDeclaresNone() throws Exception {
+               var c = MockRestClient.buildLax(HostSeedsRoleGuardOnly.class);
+               c.get("/rg2/me").run().assertStatus(403);
+               
c.get("/rg2/me").roles("admin").accept("text/plain").run().assertStatus(200).assertContent("me");
+       }
+
+       
//------------------------------------------------------------------------------------------------------------
+       // a03: rolesDeclared accumulates across host seed AND child, into the 
single shared declared-role set used
+       // by every RoleBasedRestGuard in the chain (RestOpContext.guards()) -- 
the child's own rolesDeclared="admin"
+       // feeds the host-seeded PATTERN-based roleGuard="ad*", which has no 
declared-role names of its own.
+       
//------------------------------------------------------------------------------------------------------------
+
+       @Rest(path="/rg3", rolesDeclared="admin")
+       public static class ChildDeclaresRolesOnly {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildDeclaresRolesOnly.class, 
roleGuard="ad*"))
+       public static class HostSeedsPatternRoleGuard extends 
BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a03_rolesDeclaredFromChildFeedsHostSeededPatternRoleGuard() 
throws Exception {
+               var c = 
MockRestClient.buildLax(HostSeedsPatternRoleGuard.class);
+               c.get("/rg3/me").run().assertStatus(403);
+               
c.get("/rg3/me").roles("admin").accept("text/plain").run().assertStatus(200).assertContent("me");
+       }
+}
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_Scalars_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_Scalars_Test.java
new file mode 100644
index 0000000000..53e8b27537
--- /dev/null
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/child/ChildInheritance_Scalars_Test.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.child;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.nio.charset.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.rest.mock.classic.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.servlet.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Phase 4 &mdash; proves the CHILD-WINS scalar shape for {@code 
@Child(defaultCharset=...)} and
+ * {@code @Child(maxInput=...)}, both of which resolve through {@code 
RestContext.mergeReplacedStringAttribute}
+ * (last-non-sentinel-wins over the annotation chain), exactly like {@code 
callLogger}/{@code partSerializer}/
+ * {@code partParser}.
+ *
+ * <p>
+ * {@code defaultAccept}/{@code defaultContentType} are intentionally NOT seed 
slots on {@code @Child} (see
+ * {@link Child} javadoc): they resolve through the {@code 
defaultRequestHeaders} memoizer's
+ * {@code HttpHeaderList.setDefault(...)} first-wins semantics rather than 
{@code mergeReplacedStringAttribute},
+ * which would make the host's seed win over the child's own explicit 
declaration &mdash; the opposite of the
+ * child-wins contract. That's pre-existing framework behavior unrelated to 
{@code @Child} (a plain
+ * superclass/subclass {@code @Rest} hierarchy exhibits the same 
ancestor-wins-over-descendant behavior), so
+ * these two members are deferred rather than shipped as a contract violation.
+ */
+class ChildInheritance_Scalars_Test extends TestBase {
+
+       
//==================================================================================================================
+       // defaultCharset -- true child-wins (via mergeReplacedStringAttribute)
+       
//==================================================================================================================
+
+       @Rest(path="/nocharset")
+       public static class ChildNoCharsetDeclared {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildNoCharsetDeclared.class, 
defaultCharset="utf-16"))
+       public static class HostSeedsCharset extends BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a01_charset_childWithNoDeclarationReceivesSeed() {
+               MockRestClient.buildLax(HostSeedsCharset.class);
+               var hostCtx = 
RestContext.getGlobalRegistry().get(HostSeedsCharset.class);
+               var childCtx = 
hostCtx.getRestChildren().asMap().get("nocharset");
+               assertNotNull(childCtx);
+               var opCtx = childCtx.getRestOperations().getOpContexts().get(0);
+               assertEquals(StandardCharsets.UTF_16, opCtx.getDefaultCharset(),
+                       "Child with no defaultCharset declaration must receive 
the host's seeded utf-16");
+       }
+
+       @Rest(path="/owncharset", defaultCharset="utf-16")
+       public static class ChildWithOwnCharset {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildWithOwnCharset.class, 
defaultCharset="iso-8859-1"))
+       public static class HostSeedsCharsetButChildWins extends 
BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a02_charset_childsOwnDeclarationWins() {
+               MockRestClient.buildLax(HostSeedsCharsetButChildWins.class);
+               var hostCtx = 
RestContext.getGlobalRegistry().get(HostSeedsCharsetButChildWins.class);
+               var childCtx = 
hostCtx.getRestChildren().asMap().get("owncharset");
+               assertNotNull(childCtx);
+               var opCtx = childCtx.getRestOperations().getOpContexts().get(0);
+               assertEquals(StandardCharsets.UTF_16, opCtx.getDefaultCharset(),
+                       "Child's own explicit defaultCharset must win over the 
host's seeded iso-8859-1");
+       }
+
+       
//==================================================================================================================
+       // maxInput -- true child-wins (via mergeReplacedStringAttribute); 
seed-applies-when-silent already covered by
+       // ChildContext_Seed_Test.a04 (seed vs. DefaultConfig fallback) -- this 
file adds the child-wins-when-explicit half.
+       
//==================================================================================================================
+
+       @Rest(path="/ownmax", maxInput="111")
+       public static class ChildWithOwnMaxInput {
+               @RestGet(path="/me") public String me() { return "me"; }
+       }
+
+       @Rest(childrenDefs=@Child(type=ChildWithOwnMaxInput.class, 
maxInput="222"))
+       public static class HostSeedsMaxInputButChildWins extends 
BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a03_maxInput_childsOwnDeclarationWins() {
+               MockRestClient.buildLax(HostSeedsMaxInputButChildWins.class);
+               var hostCtx = 
RestContext.getGlobalRegistry().get(HostSeedsMaxInputButChildWins.class);
+               var childCtx = hostCtx.getRestChildren().asMap().get("ownmax");
+               assertNotNull(childCtx);
+               var opCtx = childCtx.getRestOperations().getOpContexts().get(0);
+               assertEquals(111L, opCtx.getMaxInput(),
+                       "Child's own explicit maxInput must win over the host's 
seeded 222");
+       }
+}
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/ChildAnnotation_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/ChildAnnotation_Test.java
new file mode 100644
index 0000000000..5fd543d0ce
--- /dev/null
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/ChildAnnotation_Test.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.server;
+
+import static org.apache.juneau.BasicTestUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.marshall.uon.*;
+import org.apache.juneau.rest.server.auth.*;
+import org.apache.juneau.rest.server.converter.*;
+import org.apache.juneau.rest.server.logger.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Tests for the {@link ChildAnnotation} companion (programmatic builder + 
synthetic impl) and equivalency with
+ * the declarative {@link Child @Child} annotation form.
+ */
+@SuppressWarnings({
+       "unchecked" // Generic Class<? extends X>[] varargs in the builder slot 
setters are populated with single literals in tests.
+})
+class ChildAnnotation_Test extends TestBase {
+
+       public static class FooChild {}
+
+       private static ChildAnnotation.Builder fullBuilder() {
+               return ChildAnnotation.create()
+                       .type(FooChild.class)
+                       .guards(BearerTokenGuard.class)
+                       .roleGuard("admin")
+                       .rolesDeclared("admin,user")
+                       .converters(Traversable.class)
+                       .callLogger(BasicCallLogger.class)
+                       .partSerializer(UonSerializer.class)
+                       .partParser(UonParser.class)
+                       .debug(null)  // null coalesces to 
DebugAnnotation.DEFAULT
+                       .defaultCharset("utf-8")
+                       .maxInput("1M");
+       }
+
+       Child a1 = fullBuilder().build();
+       Child a2 = fullBuilder().build();
+
+       @Test void a01_stringAccessors() {
+               assertEquals(FooChild.class, a1.type());
+               assertEquals("admin", a1.roleGuard());
+               assertEquals("admin,user", a1.rolesDeclared());
+               assertEquals("utf-8", a1.defaultCharset());
+               assertEquals("1M", a1.maxInput());
+       }
+
+       @Test void a01b_classAndAnnotationAccessors() {
+               assertEquals(BearerTokenGuard.class, a1.guards()[0]);
+               assertEquals(Traversable.class, a1.converters()[0]);
+               assertEquals(BasicCallLogger.class, a1.callLogger());
+               assertEquals(UonSerializer.class, a1.partSerializer());
+               assertEquals(UonParser.class, a1.partParser());
+               // debug(null) coalesced to the default @Debug.
+               assertEquals(DebugAnnotation.DEFAULT, a1.debug());
+       }
+
+       @Test void a02_testEquivalency() {
+               assertEquals(a2, a1);
+               assertNotEqualsAny(a1.hashCode(), 0, -1);
+               assertEquals(a1.hashCode(), a2.hashCode());
+       }
+
+       @Test void a03_defaultInstance() {
+               var d = ChildAnnotation.DEFAULT;
+               assertEquals("", d.roleGuard());
+               assertEquals(0, d.guards().length);
+       }
+
+       @Test void a04_debugNonNullValueKept() {
+               // The non-null branch of debug(Debug): a supplied @Debug is 
kept as-is (not coalesced to DEFAULT).
+               var dbg = DebugAnnotation.create().value("always").build();
+               var m = 
ChildAnnotation.create().type(FooChild.class).debug(dbg).build();
+               assertEquals(dbg, m.debug());
+               assertEquals("always", m.debug().value());
+       }
+
+       // Comparison with the declarative @Child form.
+
+       @Rest(childrenDefs=@Child(
+               type=FooChild.class,
+               guards=BearerTokenGuard.class,
+               roleGuard="admin",
+               rolesDeclared="admin,user",
+               converters=Traversable.class,
+               callLogger=BasicCallLogger.class,
+               partSerializer=UonSerializer.class,
+               partParser=UonParser.class,
+               defaultCharset="utf-8",
+               maxInput="1M"
+       ))
+       public static class D1 {}
+
+       @Test void d01_comparisonWithDeclarativeAnnotation() {
+               // The builder-produced a1 must be equal+hashCode-equal to the 
declarative @Child form (same slots).
+               var d1 = 
D1.class.getAnnotationsByType(Rest.class)[0].childrenDefs()[0];
+               assertEquals(a1, d1);
+               assertEquals(a1.hashCode(), d1.hashCode());
+       }
+}
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/LazyChildren_ChildDefs_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/LazyChildren_ChildDefs_Test.java
new file mode 100644
index 0000000000..e06545d2ca
--- /dev/null
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/LazyChildren_ChildDefs_Test.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.server;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.rest.mock.classic.*;
+import org.apache.juneau.rest.server.logger.*;
+import org.apache.juneau.rest.server.servlet.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Phase 6 &mdash; proves a {@code @Rest(lazyChildren="true", 
childrenDefs=@Child(...))} child receives its
+ * host-seeded settings on first-request materialization, not just when 
eagerly built. Mirrors
+ * {@link LazyChildren_Test}'s style/package (same package so it lives 
alongside the base lazy-children suite).
+ */
+class LazyChildren_ChildDefs_Test extends TestBase {
+
+       public static class SeedLogger extends CallLogger {
+               public SeedLogger(org.apache.juneau.commons.inject.BeanStore 
bs) { super(bs); }
+       }
+
+       @Rest(path="/lazychild")
+       public static class LazyChildResource {
+               @RestGet("/ping") public String ping() { return "lazy-pong"; }
+       }
+
+       @Rest(
+               path="/root",
+               lazyChildren="true",
+               childrenDefs=@Child(type=LazyChildResource.class, 
callLogger=SeedLogger.class)
+       )
+       public static class LazySeedParent extends BasicRestServletGroup {
+               private static final long serialVersionUID = 1L;
+       }
+
+       @Test void a01_seedAppliesAfterLazyMaterialization() throws Exception {
+               var parent = new LazySeedParent();
+               var client = MockRestClient.createLax(parent).build();
+               var rc = parent.getContext();
+
+               assertTrue(rc.isLazyChildren());
+
+               // Before first request: registered for routing but not yet 
materialized.
+               var entries = rc.getRestChildren().getLazyEntries();
+               var entry = entries.get("lazychild");
+               assertNotNull(entry, "Lazy entry key should be 'lazychild'");
+               assertFalse(entry.isMaterialized(), "Should not be materialized 
before first request");
+
+               // First request triggers materialization.
+               
client.get("/lazychild/ping").run().assertStatus(200).assertContent("lazy-pong");
+               assertTrue(entry.isMaterialized(), "Should be materialized 
after first request");
+
+               // The materialized context must carry the host's seeded 
callLogger -- proving the ResolvedChild
+               // (including its @Child seed) survived deferred construction, 
not just eager construction.
+               // (Materialized lazy children live on the LazyChildEntry 
itself, not in RestChildren.asMap() --
+               // that map holds only eagerly-built children.)
+               var childCtx = entry.materialized;
+               assertNotNull(childCtx);
+               assertInstanceOf(SeedLogger.class, childCtx.getCallLogger(),
+                       "Lazily-materialized child must receive the host's 
seeded SeedLogger, same as an eager child would");
+       }
+}
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/Child.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/Child.java
new file mode 100644
index 0000000000..1edce8670f
--- /dev/null
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/Child.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.server;
+
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.*;
+
+import org.apache.juneau.marshall.httppart.*;
+import org.apache.juneau.rest.server.converter.*;
+import org.apache.juneau.rest.server.guard.*;
+import org.apache.juneau.rest.server.logger.*;
+
+/**
+ * Host-side setting seed for the {@link Rest#childrenDefs() 
@Rest(childrenDefs=...)} attribute.
+ *
+ * <p>
+ * Declares a routed child class <b>and</b> lets the <i>host</i> seed a 
curated set of {@code @Rest}-level
+ * settings onto that child's otherwise-isolated {@link RestContext} &mdash; 
the child-resource analog of
+ * {@link Rest#mixinDefs() @Rest(mixinDefs=...)}. This is the host-side 
complement to
+ * {@link Rest#children() @Rest(children=...)} (which takes bare classes and 
offers no seed hook).
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ *     <ja>@Rest</ja>(
+ *             childrenDefs=<ja>@Child</ja>(type=FooChild.<jk>class</jk>, 
callLogger=SeedLogger.<jk>class</jk>)
+ *     )
+ *     <jk>public class</jk> MyResource { ... }
+ * </p>
+ *
+ * <h5 class='section'>Seed semantics</h5>
+ * <p>
+ * Unlike {@link Mixin @Mixin} overrides (which win over an <i>inherited</i> 
chain), children are
+ * <b>isolated</b> from the host's resolution chain by design, so a {@code 
@Child} seed doesn't override
+ * anything &mdash; it seeds settings onto an otherwise-isolated child context:
+ * <ul>
+ *     <li><b>Additive-security</b> ({@code guards}, {@code converters}, 
{@code roleGuard}, {@code rolesDeclared})
+ *             &mdash; the host contributes, the child can't remove or weaken 
it (list-shaped members prepend; the two
+ *             role-based members AND-stack alongside the child's own value).
+ *     <li><b>Child-wins scalars</b> ({@code callLogger}, {@code 
partSerializer}, {@code partParser}, {@code debug},
+ *             {@code defaultCharset}, {@code maxInput}) &mdash; the seed is a 
default/fallback; the child's own explicit
+ *             {@code @Rest} declaration wins when present.
+ * </ul>
+ *
+ * <p>
+ * A child's own {@code @Rest(noInherit="&lt;property&gt;")} cuts the 
corresponding {@code @Child} seed too,
+ * for both buckets above &mdash; the child always stays in full control of 
its own configuration.
+ *
+ * <p>
+ * There is no {@code noInherit()} member on {@code @Child} itself (nothing to 
cut in an isolated context), and
+ * no {@code path()}/{@code paths()} re-mount member (the child's own {@code 
@Rest(path)} stays authoritative).
+ *
+ * <h5 class='section'>See Also:</h5><ul>
+ *     <li class='ja'>{@link Rest#childrenDefs()}
+ *     <li class='ja'>{@link Rest#children()}
+ * </ul>
+ *
+ * @since 10.0.0
+ */
+@Target({})
+@Retention(RUNTIME)
+public @interface Child {
+
+       /**
+        * The child class to route.
+        *
+        * <p>
+        * Required. Equivalent to a bare entry in {@link Rest#children()}, but 
with the seed slots below.
+        *
+        * @return The child class.
+        */
+       Class<?> type();
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Additive-security seed slots — host contributes, child can't remove 
or weaken.
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       /**
+        * Host-seeded {@link Rest#guards() guards} for this child's endpoints 
(prepended before the child's own).
+        *
+        * @return The annotation value.
+        */
+       Class<? extends RestGuard>[] guards() default {};
+
+       /**
+        * Host-seeded {@link Rest#converters() converters} for this child's 
endpoints (prepended before the child's own).
+        *
+        * @return The annotation value.
+        */
+       Class<? extends RestConverter>[] converters() default {};
+
+       /**
+        * Host-seeded {@link Rest#roleGuard() roleGuard} for this child's 
endpoints (AND-stacks with the child's own).
+        *
+        * @return The annotation value.
+        */
+       String roleGuard() default "";
+
+       /**
+        * Host-seeded {@link Rest#rolesDeclared() rolesDeclared} for this 
child's endpoints (AND-stacks with the child's own).
+        *
+        * @return The annotation value.
+        */
+       String rolesDeclared() default "";
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Child-wins scalar seed slots — seed is a default/fallback; the 
child's own explicit value wins.
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       /**
+        * Host-seeded {@link Rest#callLogger() callLogger} default for this 
child's endpoints.
+        *
+        * @return The annotation value.
+        */
+       Class<? extends CallLogger> callLogger() default CallLogger.Void.class;
+
+       /**
+        * Host-seeded {@link Rest#partSerializer() partSerializer} default for 
this child's endpoints.
+        *
+        * @return The annotation value.
+        */
+       Class<? extends HttpPartSerializer> partSerializer() default 
HttpPartSerializer.Void.class;
+
+       /**
+        * Host-seeded {@link Rest#partParser() partParser} default for this 
child's endpoints.
+        *
+        * @return The annotation value.
+        */
+       Class<? extends HttpPartParser> partParser() default 
HttpPartParser.Void.class;
+
+       /**
+        * Host-seeded {@link Rest#debug() debug} default for this child's 
endpoints (each sub-field independently seeded).
+        *
+        * @return The annotation value.
+        */
+       Debug debug() default @Debug;
+
+       /**
+        * Host-seeded {@link Rest#defaultCharset() defaultCharset} default for 
this child's endpoints.
+        *
+        * @return The annotation value.
+        */
+       String defaultCharset() default "";
+
+       /**
+        * Host-seeded {@link Rest#maxInput() maxInput} default for this 
child's endpoints.
+        *
+        * @return The annotation value.
+        */
+       String maxInput() default "";
+}
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/ChildAnnotation.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/ChildAnnotation.java
new file mode 100644
index 0000000000..2c9cb127e3
--- /dev/null
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/ChildAnnotation.java
@@ -0,0 +1,225 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.server;
+
+import static org.apache.juneau.commons.utils.Shorts.*;
+
+import org.apache.juneau.commons.*;
+import org.apache.juneau.marshall.httppart.*;
+import org.apache.juneau.rest.server.converter.*;
+import org.apache.juneau.rest.server.guard.*;
+import org.apache.juneau.rest.server.logger.*;
+
+/**
+ * Utility classes and methods for the {@link Child @Child} annotation.
+ *
+ * <h5 class='section'>See Also:</h5><ul>
+ *     <li class='ja'>{@link Child}
+ *     <li class='ja'>{@link Rest#childrenDefs()}
+ * </ul>
+ *
+ * @since 10.0.0
+ */
+public class ChildAnnotation {
+
+       /**
+        * Prevents instantiation.
+        */
+       private ChildAnnotation() {}
+
+       /**
+        * Builder class.
+        */
+       @SuppressWarnings({
+               "unchecked" // Type erasure requires cast for generic 
class-array fields.
+       })
+       public static class Builder extends AnnotationObject.Builder {
+
+               Class<?> type = Object.class;
+               Class<? extends RestGuard>[] guards = new Class[0];
+               Class<? extends RestConverter>[] converters = new Class[0];
+               String roleGuard = "";
+               String rolesDeclared = "";
+               Class<? extends CallLogger> callLogger = CallLogger.Void.class;
+               Class<? extends HttpPartSerializer> partSerializer = 
HttpPartSerializer.Void.class;
+               Class<? extends HttpPartParser> partParser = 
HttpPartParser.Void.class;
+               Debug debug = DebugAnnotation.DEFAULT;
+               String defaultCharset = "";
+               String maxInput = "";
+
+               /**
+                * Constructor.
+                */
+               protected Builder() {
+                       super(Child.class);
+               }
+
+               /**
+                * Sets the {@link Child#type()} property on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder type(Class<?> value) { type = value; return 
this; }
+
+               /**
+                * Sets the {@link Child#guards()} property on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder guards(Class<? extends RestGuard>... value) { 
guards = value; return this; }
+
+               /**
+                * Sets the {@link Child#converters()} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder converters(Class<? extends RestConverter>... 
value) { converters = value; return this; }
+
+               /**
+                * Sets the {@link Child#roleGuard()} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder roleGuard(String value) { roleGuard = value; 
return this; }
+
+               /**
+                * Sets the {@link Child#rolesDeclared()} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder rolesDeclared(String value) { rolesDeclared = 
value; return this; }
+
+               /**
+                * Sets the {@link Child#callLogger()} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder callLogger(Class<? extends CallLogger> value) { 
callLogger = value; return this; }
+
+               /**
+                * Sets the {@link Child#partSerializer()} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder partSerializer(Class<? extends 
HttpPartSerializer> value) { partSerializer = value; return this; }
+
+               /**
+                * Sets the {@link Child#partParser()} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder partParser(Class<? extends HttpPartParser> 
value) { partParser = value; return this; }
+
+               /**
+                * Sets the {@link Child#debug()} property on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder debug(Debug value) { debug = value == null ? 
DebugAnnotation.DEFAULT : value; return this; }
+
+               /**
+                * Sets the {@link Child#defaultCharset()} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder defaultCharset(String value) { defaultCharset = 
value; return this; }
+
+               /**
+                * Sets the {@link Child#maxInput()} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder maxInput(String value) { maxInput = value; 
return this; }
+
+               /**
+                * Instantiates a new {@link Child @Child} object initialized 
with this builder.
+                *
+                * @return A new {@link Child @Child} object.
+                */
+               public Child build() {
+                       return new Impl(this);
+               }
+       }
+
+       @SuppressWarnings({
+               "java:S2160" // equals() inherited from AnnotationObject 
compares all annotation interface methods; subclass fields are accessed via 
those methods.
+       })
+       private static class Impl extends AnnotationObject implements Child {
+
+               private final Class<?> type;
+               private final Class<? extends RestGuard>[] guards;
+               private final Class<? extends RestConverter>[] converters;
+               private final String roleGuard;
+               private final String rolesDeclared;
+               private final Class<? extends CallLogger> callLogger;
+               private final Class<? extends HttpPartSerializer> 
partSerializer;
+               private final Class<? extends HttpPartParser> partParser;
+               private final Debug debug;
+               private final String defaultCharset;
+               private final String maxInput;
+
+               Impl(ChildAnnotation.Builder b) {
+                       super(b);
+                       type = b.type;
+                       guards = cp(b.guards);
+                       converters = cp(b.converters);
+                       roleGuard = b.roleGuard;
+                       rolesDeclared = b.rolesDeclared;
+                       callLogger = b.callLogger;
+                       partSerializer = b.partSerializer;
+                       partParser = b.partParser;
+                       debug = b.debug;
+                       defaultCharset = b.defaultCharset;
+                       maxInput = b.maxInput;
+               }
+
+               @Override /* Overridden from Child */ public Class<?> type() { 
return type; }
+               @Override /* Overridden from Child */ public Class<? extends 
RestGuard>[] guards() { return cp(guards); }
+               @Override /* Overridden from Child */ public Class<? extends 
RestConverter>[] converters() { return cp(converters); }
+               @Override /* Overridden from Child */ public String roleGuard() 
{ return roleGuard; }
+               @Override /* Overridden from Child */ public String 
rolesDeclared() { return rolesDeclared; }
+               @Override /* Overridden from Child */ public Class<? extends 
CallLogger> callLogger() { return callLogger; }
+               @Override /* Overridden from Child */ public Class<? extends 
HttpPartSerializer> partSerializer() { return partSerializer; }
+               @Override /* Overridden from Child */ public Class<? extends 
HttpPartParser> partParser() { return partParser; }
+               @Override /* Overridden from Child */ public Debug debug() { 
return debug; }
+               @Override /* Overridden from Child */ public String 
defaultCharset() { return defaultCharset; }
+               @Override /* Overridden from Child */ public String maxInput() 
{ return maxInput; }
+       }
+
+       /**
+        * Builder creator.
+        *
+        * @return A new builder.
+        */
+       public static Builder create() {
+               return new Builder();
+       }
+
+       /** Default {@link Child} instance (empty seed slots; {@code 
type=Object.class}). */
+       public static final Child DEFAULT = create().build();
+}
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/Rest.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/Rest.java
index 17c316bd53..266fe2bb51 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/Rest.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/Rest.java
@@ -223,6 +223,47 @@ public @interface Rest {
         */
        Class<?>[] children() default {};
 
+       /**
+        * Rich child definitions &mdash; the host-side seed form of {@link 
#children()}.
+        *
+        * <p>
+        * Each {@link Child @Child} entry names a child class (via {@link 
Child#type()}) <b>and</b> lets the host
+        * seed a curated set of {@code @Rest}-level settings onto that child's 
otherwise-isolated
+        * {@link RestContext} &mdash; guards, role guards, a call logger, part 
serializer/parser, debug, and a
+        * handful of default-scalar settings &mdash; without editing the child 
class.
+        *
+        * <p>
+        * Unlike {@link #mixinDefs() mixinDefs}, this does not override an 
inherited chain (children never inherit
+        * from the host); it seeds settings onto an isolated context. 
Additive-security seed members ({@code guards},
+        * {@code converters}, {@code roleGuard}, {@code rolesDeclared}) can't 
be removed or weakened by the child;
+        * the remaining scalar seed members are a fallback the child's own 
explicit {@code @Rest} declaration wins
+        * over. See {@link Child} for the full semantics.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      <ja>@Rest</ja>(
+        *              
childrenDefs=<ja>@Child</ja>(type=FooChild.<jk>class</jk>, 
callLogger=SeedLogger.<jk>class</jk>)
+        *      )
+        *      <jk>public class</jk> MyResource { ... }
+        * </p>
+        *
+        * <p>
+        * This attribute is additive to {@link #children()}: bare-class 
entries in {@code children()} and rich
+        * entries here are discovered together (bare classes first, then 
{@code childrenDefs}). A
+        * {@code @Child(type=X.class)} with no seed members is the exact 
equivalent of a bare
+        * {@code children=X.class} entry.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='ja'>{@link Child}
+        *      <li class='jm'>{@link #children()}
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/ChildResources#host-side-seeding-with-child-1000";>Child
 Resources &mdash; Host-side seeding with @Child</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        * @since 10.0.0
+        */
+       Child[] childrenDefs() default {};
+
        /**
         * REST mixins.
         *
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestAnnotation.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestAnnotation.java
index 26b241119c..b654d6f7c1 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestAnnotation.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestAnnotation.java
@@ -77,6 +77,7 @@ public class RestAnnotation {
                private Debug debug = DebugAnnotation.DEFAULT;
                private Class<? extends Serializer>[] serializers = new 
Class[0];
                private Class<?>[] children = {};
+               private Child[] childrenDefs = {};
                private Class<?>[] mixins = {};
                private Mixin[] mixinDefs = {};
                private Class<?>[] parsers = {};
@@ -216,6 +217,17 @@ public class RestAnnotation {
                        return this;
                }
 
+               /**
+                * Sets the {@link Rest#childrenDefs()} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder childrenDefs(Child...value) {
+                       childrenDefs = value;
+                       return this;
+               }
+
                /**
                 * Sets the {@link Rest#mixins()} property on this annotation.
                 *
@@ -806,6 +818,7 @@ public class RestAnnotation {
                private final Debug debug;
                private final Class<? extends Serializer>[] serializers;
                private final Class<?>[] children;
+               private final Child[] childrenDefs;
                private final Class<?>[] mixins;
                private final Mixin[] mixinDefs;
                private final Class<?>[] parsers;
@@ -862,6 +875,7 @@ public class RestAnnotation {
                        callLogger = b.callLogger;
                        authenticator = b.authenticator;
                        children = cp(b.children);
+                       childrenDefs = cp(b.childrenDefs);
                        mixins = cp(b.mixins);
                        mixinDefs = cp(b.mixinDefs);
                        clientVersionHeader = b.clientVersionHeader;
@@ -947,6 +961,11 @@ public class RestAnnotation {
                        return children;
                }
 
+               @Override /* Overridden from Rest */
+               public Child[] childrenDefs() {
+                       return childrenDefs;
+               }
+
                @Override /* Overridden from Rest */
                public Class<?>[] mixins() {
                        return mixins;
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestChildren.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestChildren.java
index 47dab35d5c..abb3e6b191 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestChildren.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestChildren.java
@@ -71,6 +71,7 @@ public class RestChildren {
         */
        static class LazyChildEntry {
 
+               final RestContext.ResolvedChild resolvedChild;
                final Class<?> resourceClass;
                final String path;
                final UrlPathMatcher pathMatcher;
@@ -86,8 +87,9 @@ public class RestChildren {
 
                private final Object lock = new Object();
 
-               LazyChildEntry(Class<?> resourceClass, String path, RestContext 
parent, BeanStore beanStore, ServletConfig servletConfig) {
-                       this.resourceClass = resourceClass;
+               LazyChildEntry(RestContext.ResolvedChild resolvedChild, String 
path, RestContext parent, BeanStore beanStore, ServletConfig servletConfig) {
+                       this.resolvedChild = resolvedChild;
+                       this.resourceClass = resolvedChild.type();
                        this.path = path;
                        var p = path;
                        if (! p.endsWith("/*"))
@@ -120,7 +122,7 @@ public class RestChildren {
                                        return rc;
                                LOGGER.info(() -> "Lazy REST child 
materializing: " + resourceClass.getName() + " at path '" + path + "'");
                                try {
-                                       rc = buildChildContext(parent, 
beanStore, servletConfig, resourceClass, null, "");
+                                       rc = buildChildContext(parent, 
beanStore, servletConfig, resolvedChild, null, "");
                                        rc.postInit();
                                        rc.postInitChildFirst();
                                } catch (Exception e) {
@@ -215,14 +217,41 @@ public class RestChildren {
                 * The path is pre-resolved from the resource class so that 
routing works immediately at parent startup.
                 * The full {@link RestContext} is built on the first matching 
request.
                 *
+                * <p>
+                * Delegates to {@link #addLazy(RestContext.ResolvedChild, 
String)} with a bare (no-seed) declaration —
+                * this public overload's signature is preserved unchanged (it 
has no external-caller-visible way to
+                * express a host-declared {@code @Child} seed; see that 
overload for the seed-carrying entry point used
+                * internally by {@link RestContext}'s {@code childrenDefs} 
discovery).
+                *
                 * @param resourceClass The resource class to materialize 
lazily. Must not be {@code null}.
                 * @param path The pre-resolved path prefix (without leading 
slash). Use {@code ""} to read from
                 *      {@link Rest#path() @Rest(path)} on the resource class.
                 * @return This object.
                 */
                public Builder addLazy(Class<?> resourceClass, String path) {
+                       return 
addLazy(RestContext.ResolvedChild.ofBare(resourceClass), path);
+               }
+
+               /**
+                * Registers a lazy child resource entry carrying a normalized 
child declaration, which may include a
+                * host-declared {@code @Child} seed.
+                *
+                * <p>
+                * The path is pre-resolved from the resource class so that 
routing works immediately at parent startup.
+                * The full {@link RestContext} is built on the first matching 
request, with the seed (if any) threaded
+                * through to {@link RestChildren#buildChildContext} at that 
point — so a lazily-materialized child
+                * receives its host-seeded settings identically to an 
eagerly-built one.
+                *
+                * @param resolvedChild The normalized child declaration (class 
plus any host-declared seed). Must not be
+                *      {@code null}.
+                * @param path The pre-resolved path prefix (without leading 
slash). Use {@code ""} to read from
+                *      {@link Rest#path() @Rest(path)} on the resource class.
+                * @return This object.
+                */
+               Builder addLazy(RestContext.ResolvedChild resolvedChild, String 
path) {
+                       var resourceClass = resolvedChild.type();
                        var resolvedPath = isNotEmpty(path) ? 
trimLeadingSlashes(path) : LazyChildEntry.resolvePathForClass(resourceClass);
-                       lazyList.add(new LazyChildEntry(resourceClass, 
resolvedPath, parent, beanStore, servletConfig));
+                       lazyList.add(new LazyChildEntry(resolvedChild, 
resolvedPath, parent, beanStore, servletConfig));
                        return this;
                }
 
@@ -553,7 +582,8 @@ public class RestChildren {
         * @param parent The parent {@link RestContext}. Must not be {@code 
null}.
         * @param beanStore The bean store to resolve instances and 
dependencies from. Must not be {@code null}.
         * @param servletConfig The {@link ServletConfig} to pass through to 
the child. May be {@code null}.
-        * @param resourceClass The resource class. Must not be {@code null}.
+        * @param resolvedChild The normalized child declaration (class plus 
any host-declared {@code @Child} seed).
+        *      Must not be {@code null}.
         * @param resourceInstance A pre-supplied instance of the resource. If 
{@code null}, the instance is resolved
         *      from the bean store or freshly instantiated via {@link 
BeanInstantiator}.
         * @param pathOverride An explicit path segment (relative to the 
parent). Use {@code ""} to read the path from
@@ -565,9 +595,10 @@ public class RestChildren {
                        RestContext parent,
                        BeanStore beanStore,
                        ServletConfig servletConfig,
-                       Class<?> resourceClass,
+                       RestContext.ResolvedChild resolvedChild,
                        Object resourceInstance,
                        String pathOverride) throws Exception {
+               var resourceClass = resolvedChild.type();
                Supplier<?> so;
                if (resourceInstance != null) {
                        final Object r = resourceInstance;
@@ -578,7 +609,7 @@ public class RestChildren {
                        Object o = BeanInstantiator.of(resourceClass, 
beanStore).run();
                        so = () -> o;
                }
-               var cc = new RestContext(new RestContext.Args(resourceClass, 
parent, servletConfig, so, pathOverride, null, null, null, 
RestContext.ContextKind.CHILD));
+               var cc = new RestContext(new RestContext.Args(resourceClass, 
parent, servletConfig, so, pathOverride, null, null, null, new 
RestContext.ContextKind.Child(resolvedChild)));
                var mi = ClassInfo.of(so.get())
                        .getMethod(x -> x.hasName("setContext") && 
x.hasParameterTypes(RestContext.class))
                        .orElse(null);
@@ -592,7 +623,8 @@ public class RestChildren {
                        throw isex("Cannot add a child to a RestChildren that 
was not initialized with a parent RestContext.");
                RestContext cc;
                try {
-                       cc = buildChildContext(parent, beanStore, 
servletConfig, resourceClass, resourceInstance, pathOverride);
+                       // The dynamic-add API has no seed — a bare 
ResolvedChild preserves its existing no-override behavior.
+                       cc = buildChildContext(parent, beanStore, 
servletConfig, RestContext.ResolvedChild.ofBare(resourceClass), 
resourceInstance, pathOverride);
                } catch (Exception e) {
                        throw new ServletException("Failed to build child REST 
context for " + resourceClass.getName(), unwrapThrowable(e));
                }
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestContext.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestContext.java
index 5ebaacb114..9386a298ce 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestContext.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestContext.java
@@ -222,9 +222,10 @@ public class RestContext extends Context {
         *      {@code getPaths()} getter, then {@link Rest#paths()} 
(SVL-resolved per element and comma-split).
         *      An empty array explicitly clears the mount list (no top-level 
mounts).
         * @param kind The build-time {@link ContextKind kind} of this context 
— {@link ContextKind#ROOT ROOT} for a
-        *      top-level/host/mock resource, {@link ContextKind#CHILD CHILD} 
for a {@link Rest#children() @Rest(children)}
-        *      sub-resource (isolated resolution), or a {@link 
ContextKind.Mixin MIXIN} carrying the {@link ResolvedMixin}
-        *      that produced a per-mixin sub-context (parent-linked to the 
host so that
+        *      top-level/host/mock resource, a {@link ContextKind.Child CHILD} 
carrying the {@link ResolvedChild} for a
+        *      {@link Rest#children() @Rest(children)}/{@link 
Rest#childrenDefs() childrenDefs} sub-resource (isolated
+        *      resolution, with an optional host-seeded payload), or a {@link 
ContextKind.Mixin MIXIN} carrying the
+        *      {@link ResolvedMixin} that produced a per-mixin sub-context 
(parent-linked to the host so that
         *      {@link RestContext#getRestAnnotationsForProperty(String) 
annotation-property walks} prepend the host's
         *      {@code @Rest} chain before the mixin's own).
         *
@@ -354,6 +355,62 @@ public class RestContext extends Context {
                }
        }
 
+       /**
+        * Normalized carrier for a routed child, produced by discovery from 
either a bare {@link Rest#children()}
+        * class or a rich {@link Rest#childrenDefs() @Child} declaration — the 
exact structural analog of
+        * {@link ResolvedMixin}.
+        *
+        * <p>
+        * A bare class is normalized to {@code new ResolvedChild(type, 
ChildAnnotation.DEFAULT)} (i.e.
+        * {@code @Child(type=X)} with an empty seed), so a bare class and an 
empty-seed {@code @Child} are
+        * structurally identical — the same equivalence {@link ResolvedMixin} 
establishes for mixins.
+        *
+        * @param type The child class.
+        * @param seed The host-declared seed annotation, or {@link 
ChildAnnotation#DEFAULT} for a bare class.
+        *
+        * @since 10.0.0
+        */
+       public static record ResolvedChild(Class<?> type, Child seed) {
+
+               /**
+                * Compact canonical constructor — validates required 
components and null-coalesces {@code seed}.
+                */
+               public ResolvedChild {
+                       assertArgNotNull("type", type);
+                       if (seed == null)
+                               seed = ChildAnnotation.DEFAULT;
+               }
+
+               /**
+                * Normalizes a bare child class to a {@link ResolvedChild} 
with no seed.
+                *
+                * @param type The child class.
+                * @return A {@link ResolvedChild} carrying {@link 
ChildAnnotation#DEFAULT} as its seed.
+                */
+               static ResolvedChild ofBare(Class<?> type) {
+                       return new ResolvedChild(type, ChildAnnotation.DEFAULT);
+               }
+
+               /**
+                * Normalizes a rich {@code @Child} declaration to a {@link 
ResolvedChild}.
+                *
+                * @param c The {@code @Child} annotation.
+                * @return A {@link ResolvedChild} carrying {@code c} as its 
seed.
+                */
+               static ResolvedChild ofDef(Child c) {
+                       return new ResolvedChild(c.type(), c);
+               }
+
+               /**
+                * Returns {@code true} when this child carries no 
host-declared seed (i.e. is equivalent to a bare class).
+                *
+                * @return {@code true} if the seed payload is {@link 
ChildAnnotation#DEFAULT}.
+                */
+               boolean hasNoSeed() {
+                       return seed == ChildAnnotation.DEFAULT;
+               }
+       }
+
        /**
         * The build-time <i>kind</i> of a {@link RestContext} — which of the 
three construction flavors produced it.
         *
@@ -368,11 +425,14 @@ public class RestContext extends Context {
         * parent-linked to the host and <i>inherits</i> the host's {@code 
@Rest} annotation chain
         * ({@link #getRestAnnotationsForProperty(String)} walks 
host&rarr;mixin), so {@code @Mixin} overrides
         * <i>layer on top of</i> an inherited chain.  A {@link Child} is 
<i>isolated</i> ({@link #HOST_ONLY_PROPERTIES}
-        * includes {@code children}; children retain pre-10.0.0 isolated 
resolution with no parent walk).  So a future
-        * {@code @Child} would <i>seed</i> settings onto an isolated context 
rather than <i>override</i> an inherited
-        * chain, and would have no {@code noInherit} analog — it must NOT 
reuse the mixin override-resolution path.
-        * The {@code Child} variant is intentionally shaped so it can later 
widen to reference a {@code ResolvedChild}
-        * (mirroring how {@code Mixin} references a {@code ResolvedMixin}) 
without re-plumbing this discriminator.
+        * includes {@link RestServerConstants#PROPERTY_children}; children 
retain pre-10.0.0 isolated resolution with
+        * no parent walk).  A {@code @Child} seed therefore does not 
<i>override</i> an inherited chain the way a
+        * {@code @Mixin} override does — instead, {@link 
#computeRawRestAnnotations()} injects the seed's synthetic
+        * {@code @Rest} at the <b>least-derived</b> slot of the child's own 
(still fully isolated) annotation chain,
+        * between the child's real {@code @Rest} declarations and the 
synthesized {@link
+        * org.apache.juneau.rest.server.config.DefaultConfig DefaultConfig} 
fallback.  This ranks the seed below
+        * everything the child itself declares (so the child's own explicit 
value wins for child-wins scalars, and
+        * the child's own {@code noInherit} cutoff naturally cuts the seed 
too) but above the framework defaults.
         *
         * @since 10.0.0
         */
@@ -381,8 +441,14 @@ public class RestContext extends Context {
                /** A top-level (host) resource, mock context, or 
servlet-mounted resource. */
                record Root() implements ContextKind {}
 
-               /** A routed child resource (mounted via {@link Rest#children() 
@Rest(children)}); isolated resolution. */
-               record Child() implements ContextKind {}
+               /**
+                * A routed child resource (mounted via {@link 
Rest#children()}/{@link Rest#childrenDefs()}); isolated
+                * resolution, with an optional host-seeded {@code @Child} 
payload injected at the least-derived slot of
+                * its own annotation chain (see the class javadoc above).
+                *
+                * @param def The normalized declaration that produced this 
sub-context — carries the host-declared seed payload.
+                */
+               record Child(ResolvedChild def) implements ContextKind {}
 
                /**
                 * A per-mixin sub-context (composed via {@link 
Rest#mixins()}/{@link Rest#mixinDefs()}); inherits the host chain.
@@ -393,9 +459,6 @@ public class RestContext extends Context {
 
                /** Shared {@link Root} singleton (the common case). */
                ContextKind ROOT = new Root();
-
-               /** Shared {@link Child} singleton (the path override travels 
in {@link Args#path()}, not here). */
-               ContextKind CHILD = new Child();
        }
 
        /**
@@ -836,6 +899,8 @@ public class RestContext extends Context {
        protected final ContextKind contextKind;
        /** The normalized mixin declaration that produced this sub-context, or 
{@code null} when not a mixin context. */
        protected final ResolvedMixin resolvedMixin;
+       /** The normalized child declaration that produced this sub-context, or 
{@code null} when not a child context. */
+       protected final ResolvedChild resolvedChild;
        protected final String fullPath;
        protected final String path;
        protected final String[] paths;
@@ -875,6 +940,15 @@ public class RestContext extends Context {
         */
        private ResolvedMixin resolvedMixinField() { return resolvedMixin; }
 
+       /**
+        * Field accessor for {@link #resolvedChild}, callable from memoizer 
field-initializer lambdas (which run
+        * before the blank-final field is read directly).  Returns the 
normalized child declaration that produced
+        * this sub-context, or {@code null} when this is not a child context.
+        *
+        * @return The resolved child, or {@code null}.
+        */
+       private ResolvedChild resolvedChildField() { return resolvedChild; }
+
        /**
         * Creates the bean store for this context.
         *
@@ -2390,15 +2464,61 @@ public class RestContext extends Context {
                }
        }
 
+       /**
+        * Discovers all children declared on this resource's {@code @Rest} 
chain, normalized to {@link ResolvedChild}.
+        *
+        * <p>
+        * Reads both {@link Rest#children() bare-class} and {@link 
Rest#childrenDefs() rich &#64;Child} entries (bare
+        * first, then {@code childrenDefs}). First occurrence of a class wins 
for ordering; a rich {@code @Child}
+        * <b>upgrades</b> an earlier bare entry for the same class (a bare 
class is just {@code @Child(type=X)} with
+        * an empty seed, so upgrading only adds the host-declared seed).
+        *
+        * <p>
+        * Deliberately <b>non-recursive</b>, unlike {@link 
#getResolvedMixins()}: children do not flatten transitively
+        * to the host — each child resolves its own nested {@code 
@Rest(children=...)}/{@code childrenDefs} independently
+        * when its own {@link RestContext} is constructed.
+        *
+        * @return The ordered, de-duplicated normalized children, never {@code 
null}.
+        */
+       private Collection<ResolvedChild> getResolvedChildren() {
+               var out = new LinkedHashMap<Class<?>,ResolvedChild>();
+               getRestAnnotations().forEach(ai -> {
+                       for (var c : ai.inner().children())
+                               collectResolvedChild(ResolvedChild.ofBare(c), 
out);
+                       for (var def : ai.inner().childrenDefs())
+                               collectResolvedChild(ResolvedChild.ofDef(def), 
out);
+               });
+               return out.values();
+       }
+
+       private void collectResolvedChild(ResolvedChild rc, 
LinkedHashMap<Class<?>,ResolvedChild> out) {
+               var type = rc.type();
+               // type==null is defensive only — @Child.type() is required and 
a declarative @Child requires a class
+               // literal, so it is never null in practice.  
type==resourceClass() guards a self-reference (skip to
+               // avoid an infinite loop).
+               if (type == null || type == resourceClass())  // HTT: the null 
arm is not reachable via the annotation API.
+                       return;
+               var existing = out.get(type);
+               if (existing != null) {
+                       // Already discovered.  A rich @Child upgrades a bare 
entry (in place, preserving position); otherwise
+                       // the first occurrence wins.  No recursion into the 
child's own nested children (see javadoc above).
+                       if (existing.hasNoSeed() && ! rc.hasNoSeed())
+                               out.put(type, rc);
+                       return;
+               }
+               out.put(type, rc);  // No recursion into type's own children — 
each child owns its own subtree.
+       }
+
        /**
         * The {@link RestChildren} for this resource — child {@link 
RestContext} instances registered via
-        * {@link Rest#children() @Rest(children)}.
+        * {@link Rest#children() @Rest(children)}/{@link Rest#childrenDefs() 
childrenDefs}.
         *
         * <p>
-        * Reads child classes directly from the {@code @Rest(children)} 
annotation chain (parent-to-child),
-        * deduplicates, instantiates each child via the bean store, and builds 
a {@link RestContext} for each.
-        * Eagerly initialized in the constructor (via an explicit {@code 
.get()} call inside the try-catch
-        * block) so that any construction failure surfaces at initialization 
time rather than lazily.
+        * Reads children (bare and rich) directly from the {@code @Rest} 
annotation chain via
+        * {@link #getResolvedChildren()}, instantiates each child via the bean 
store, and builds a {@link RestContext}
+        * for each, threading through any host-declared {@code @Child} seed. 
Eagerly initialized in the constructor
+        * (via an explicit {@code .get()} call inside the try-catch block) so 
that any construction failure surfaces
+        * at initialization time rather than lazily.
         */
        @SuppressWarnings({
                "java:S3776" // cognitive complexity acceptable for 
child-context construction
@@ -2408,22 +2528,16 @@ public class RestContext extends Context {
                var servletConfig = 
bs.getBean(ServletConfig.class).orElse(null);
                var b = RestChildren.create(this, bs, servletConfig);
 
-               // Collect child classes from @Rest(children) on the annotation 
chain (parent-to-child order).
-               // Deduplicate so the same child class registered on both a 
parent and child annotation
-               // doesn't create two contexts.
-               var seen = new LinkedHashSet<Class<?>>();
-               getRestAnnotations().forEach(ai -> 
seen.addAll(Arrays.asList(ai.inner().children())));
-
                var lazy = isLazyChildren();
 
-               for (var rc2 : seen) {
-                       if (rc2 == resourceClass())
-                               continue;  // Guard against self-reference 
infinite loop.
+               for (var rc2 : getResolvedChildren()) {
                        if (lazy) {
                                // Lazy: register a routing stub now; defer 
full RestContext construction to first request.
+                               // The stub carries the full ResolvedChild 
(including any host-declared @Child seed), so a
+                               // lazily-materialized child receives its seed 
on first invocation identically to an eager one.
                                b.addLazy(rc2, "");
                        } else {
-                               // Eager (default): build the full child 
RestContext immediately.
+                               // Eager (default): build the full child 
RestContext immediately, seed included.
                                b.add(RestChildren.buildChildContext(this, bs, 
servletConfig, rc2, null, ""));
                        }
                }
@@ -2474,6 +2588,7 @@ public class RestContext extends Context {
                        contextKind = builder.args.kind();
                        isMixinContext = contextKind instanceof 
ContextKind.Mixin;
                        resolvedMixin = (contextKind instanceof 
ContextKind.Mixin contextKind2) ? contextKind2.def() : null;
+                       resolvedChild = (contextKind instanceof 
ContextKind.Child contextKind3) ? contextKind3.def() : null;
                        resourceClass = builder.resourceClass;
                        var rs = new ResourceSupplier(resourceClass, 
assertArgNotNull("resource", builder.args.resource()));
                        resource = rs;
@@ -2880,6 +2995,51 @@ public class RestContext extends Context {
                return b.build();
        }
 
+       /**
+        * The host-declared {@code @Child} seed values translated into a 
synthetic {@link Rest} annotation, for a
+        * child sub-context that carries a seed. {@code null} for non-child 
contexts and for children with no seed
+        * (bare classes).
+        *
+        * <p>
+        * Built once at memoizer-init time (zero per-request cost). Unlike 
{@link #mixinOverrideAnnotation}, this is
+        * injected at the <b>least-derived</b> slot of {@link 
#computeRawRestAnnotations()} — below the child's own
+        * real {@code @Rest} declarations but above the synthesized {@code 
DefaultConfig} fallback — so the seed acts
+        * as a default/fallback the child's own explicit configuration wins 
over (see {@link ContextKind} javadoc).
+        */
+       private final Memoizer<AnnotationInfo<Rest>> childSeedAnnotation = 
memoizer(() -> {
+               var rc = resolvedChildField();
+               if (rc == null || rc.hasNoSeed())
+                       return null;
+               var rest = buildChildSeedRest(rc.seed());
+               return AnnotationInfo.of(ClassInfo.of(getResourceClass()), 
rest);
+       });
+
+       /**
+        * Translates a host-declared {@link Child @Child} into a synthetic 
{@link Rest @Rest} carrying only its
+        * seed slots (the §3 scope subset — no {@code encoders}/{@code 
serializers}/{@code parsers}/
+        * {@code responseProcessors}/{@code restOpArgs}/{@code 
messages}/default headers or attributes/
+        * {@code produces}/{@code consumes}), so the standard {@code @Rest} 
resolution chain applies the seed uniformly.
+        *
+        * @param c The {@code @Child} seed declaration.
+        * @return A synthetic {@code @Rest} populated from {@code c}'s seed 
slots.
+        */
+       private static Rest buildChildSeedRest(Child c) {
+               // @formatter:off
+               var b = RestAnnotation.create()
+                       .guards(c.guards())
+                       .roleGuard(c.roleGuard())
+                       .rolesDeclared(c.rolesDeclared())
+                       .converters(c.converters())
+                       .callLogger(c.callLogger())
+                       .partSerializer(c.partSerializer())
+                       .partParser(c.partParser())
+                       .debug(c.debug())
+                       .defaultCharset(c.defaultCharset())
+                       .maxInput(c.maxInput());
+               // @formatter:on
+               return b.build();
+       }
+
        /**
         * Memoized list of every {@link Rest} annotation on the resource class 
and its supertypes, in child-to-parent order.
         *
@@ -2906,7 +3066,16 @@ public class RestContext extends Context {
 
        /**
         * Computes the raw {@code @Rest} annotation chain (most-derived 
first), folding in the framework
-        * {@code DefaultConfig} annotations for non-mixin contexts.  Does not 
include the synthetic builder annotation.
+        * {@code DefaultConfig} annotations for non-mixin contexts, and — for 
a {@linkplain ContextKind.Child child}
+        * context carrying a host-declared {@code @Child} seed — injecting the 
seed's synthetic {@code @Rest} at the
+        * least-derived slot: {@code raw ++ [seed] ++ 
defaultConfigAnnotations}. This ranks the seed below everything
+        * the child itself declares (child-wins scalars resolve to the child's 
own value when present) but above the
+        * framework's {@code DefaultConfig} fallback (the seed is never masked 
by framework defaults). This holds
+        * uniformly whether {@code DefaultConfig} is separately synthesized 
(the common case) or already embedded
+        * within {@code raw} because the child class transitively implements 
{@code DefaultConfig} itself (e.g. by
+        * extending {@code BasicRestServlet}/{@code BasicRestServletGroup}) — 
in the embedded case, {@code raw} is
+        * split around the embedded {@code DefaultConfig} entry/entries so the 
seed still lands between the child's
+        * own declarations and that entry, rather than after it. Does not 
include the synthetic builder annotation.
         *
         * @return The raw annotation chain.
         */
@@ -2914,17 +3083,49 @@ public class RestContext extends Context {
                var raw = getAnnotationProvider().find(Rest.class, 
ClassInfo.of(getResourceClass()));
                if (isMixinContextField())
                        return raw;
-               var hasDefaultConfig = raw.stream().anyMatch(ai -> 
ai.getAnnotatable() instanceof ClassInfo ci && 
DefaultConfig.class.equals(ci.inner()));
-               if (hasDefaultConfig)
-                       return raw;
+               var seed = childSeedAnnotation.get();  // null for non-child 
contexts and no-seed children
+               // The framework's DefaultConfig fallback is 
synthesized/appended for EVERY non-mixin context that
+               // doesn't already transitively implement DefaultConfig itself 
— this happens unconditionally,
+               // regardless of whether this context also carries an @Child 
seed (that baseline behavior predates
+               // and is independent of @Child). Partition raw into "the 
resource's own real declarations" and any
+               // DefaultConfig entries already embedded within its own 
hierarchy (e.g. because it extends
+               // BasicRestServlet/BasicRestServletGroup, which implement 
DefaultConfig), so that when a seed IS
+               // present it can always be inserted between them — below 
everything the child itself declares, but
+               // above DefaultConfig's fallback values, whether that fallback 
is embedded here or synthesized below.
+               var embeddedDefaultConfig = raw.stream().filter(ai -> 
ai.getAnnotatable() instanceof ClassInfo ci && 
DefaultConfig.class.equals(ci.inner())).toList();
+               if (!embeddedDefaultConfig.isEmpty()) {
+                       if (seed == null)
+                               return raw;
+                       var ownDeclarations = raw.stream().filter(ai -> 
!(ai.getAnnotatable() instanceof ClassInfo ci && 
DefaultConfig.class.equals(ci.inner()))).toList();
+                       var combined = new 
ArrayList<AnnotationInfo<Rest>>(raw.size() + 1);
+                       combined.addAll(ownDeclarations);
+                       combined.add(seed);
+                       combined.addAll(embeddedDefaultConfig);
+                       return u(combined);
+               }
                var defaultConfigAnnotations = 
getAnnotationProvider().find(Rest.class, ClassInfo.of(DefaultConfig.class));
                if (defaultConfigAnnotations.isEmpty())
-                       return raw;
+                       return seed == null ? raw : appendAnnotation(raw, seed);
                var combined = new ArrayList<>(raw);
+               if (seed != null)
+                       combined.add(seed);
                combined.addAll(defaultConfigAnnotations);
                return u(combined);
        }
 
+       /**
+        * Returns a new unmodifiable list with {@code extra} appended after 
{@code base}.
+        *
+        * @param base The base list.
+        * @param extra The entry to append.
+        * @return An unmodifiable list of {@code base} followed by {@code 
extra}.
+        */
+       private static List<AnnotationInfo<Rest>> 
appendAnnotation(List<AnnotationInfo<Rest>> base, AnnotationInfo<Rest> extra) {
+               var combined = new ArrayList<>(base);
+               combined.add(extra);
+               return u(combined);
+       }
+
        /**
         * Prepends the synthetic builder-supplied {@code @Rest} annotation at 
the most-derived (child)
         * position so its set members win in both child-first ({@code 
findFirst}) and parent-to-child
@@ -3281,7 +3482,7 @@ public class RestContext extends Context {
         * preserves that invariant when a mixin sub-context walks annotations 
for these properties.
         */
        private static final Set<String> HOST_ONLY_PROPERTIES = Set.of(
-               PROPERTY_path, PROPERTY_paths, PROPERTY_mixins, "children"
+               PROPERTY_path, PROPERTY_paths, PROPERTY_mixins, 
PROPERTY_children
        );
 
        /**
@@ -3484,6 +3685,9 @@ public class RestContext extends Context {
         * @param s The raw string. Can be {@code null}.
         * @return The resolved string.
         */
+       @SuppressWarnings({
+               "java:S2259" // getVarResolver() is never null 
post-construction: registerFrameworkDefaults() always registers a VarResolver 
default supplier before the constructor returns.
+       })
        protected String resolve(String s) {
                return getVarResolver().resolve(s);
        }
@@ -4144,6 +4348,9 @@ public class RestContext extends Context {
         *
         * @return The context statistics.
         */
+       @SuppressWarnings({
+               "java:S2259" // getMethodExecStore() is never null 
post-construction: registerFrameworkDefaults() always registers a 
MethodExecStore default supplier before the constructor returns.
+       })
        public RestContextStats getStats() { return new 
RestContextStats(startTime, getMethodExecStore().getStatsByTotalTime()); }
 
        /**
@@ -4548,6 +4755,9 @@ public class RestContext extends Context {
                return this;
        }
 
+       @SuppressWarnings({
+               "java:S2259" // mi is guarded by the nn(mi) check above; 
Sonar's flow analysis does not track nn() as a null guard.
+       })
        private void initializeResourceContext(Object resource2) {
                var mi = ClassInfo.of(resource2).getMethod(x -> 
x.hasName("setContext") && x.hasParameterTypes(RestContext.class)).orElse(null);
                if (nn(mi)) {
@@ -4565,6 +4775,9 @@ public class RestContext extends Context {
         * @return This object.
         * @throws ServletException Error occurred.
         */
+       @SuppressWarnings({
+               "java:S2259" // getRestChildren() is never null 
post-construction: registerFrameworkDefaults() always registers a RestChildren 
default supplier before the constructor returns.
+       })
        public RestContext postInitChildFirst() throws ServletException {
                if (initialized.get())
                        return this;
@@ -4580,6 +4793,9 @@ public class RestContext extends Context {
                return this;
        }
 
+       @SuppressWarnings({
+               "java:S2259" // getDebugConfig() is never null 
post-construction: registerFrameworkDefaults() always registers a DebugConfig 
default supplier before the constructor returns.
+       })
        private boolean isDebug(RestSession call) {
                return getDebugConfig().resolve(this, 
call.getRequest()).enabled();
        }
@@ -4797,6 +5013,9 @@ public class RestContext extends Context {
         * @param m The method to get statistics for.
         * @return The cached time-stats object.
         */
+       @SuppressWarnings({
+               "java:S2259" // getMethodExecStore() is never null 
post-construction: registerFrameworkDefaults() always registers a 
MethodExecStore default supplier before the constructor returns.
+       })
        protected MethodExecStats getMethodExecStats(Method m) {
                return getMethodExecStore().getStats(m);
        }
@@ -5071,7 +5290,8 @@ public class RestContext extends Context {
         * @throws NotImplemented No registered response processors could 
handle the call.
         */
        @SuppressWarnings({
-               "java:S127" // Loop counter i resets to -1 on RESTART
+               "java:S127", // Loop counter i resets to -1 on RESTART
+               "java:S2259" // getResponseProcessors() is never null 
post-construction: registerFrameworkDefaults() always registers a 
ResponseProcessor[] default supplier before the constructor returns.
        })
        public void processResponse(RestOpSession opSession) throws 
IOException, BasicHttpException, NotImplemented {
 
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestServerConstants.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestServerConstants.java
index 38d2897a63..345a2200c8 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestServerConstants.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestServerConstants.java
@@ -232,6 +232,9 @@ public final class RestServerConstants {
        /** The {@code "mixins"} annotation attribute name — used in {@code 
noInherit} matching on {@code @Rest} annotations to cut off the class-chain 
walk when resolving operation mixins. */
        public static final String PROPERTY_mixins = "mixins";
 
+       /** The {@code "children"} annotation attribute name — used in {@code 
noInherit} matching on {@code @Rest} annotations to cut off the class-chain 
walk when resolving routed children. */
+       public static final String PROPERTY_children = "children";
+
        /** The {@code "value"} annotation attribute name — used by {@code 
@RestOp}/verb annotations to hold the (optional method-prefixed) path; folded 
into {@link #PROPERTY_path}. */
        public static final String PROPERTY_value = "value";
 

Reply via email to