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

xiazcy pushed a commit to branch steps-taking-traversal-poc
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 433c2e4e3ebb9e3370b6eb9f98a22dc3de0b5e62
Author: Yang Xia <[email protected]>
AuthorDate: Wed May 27 12:10:39 2026 -0700

    add compile time check & strategy verification to prevent child traversal 
mutating steps
---
 .../tinkerpop/gremlin/process/traversal/P.java     |  15 ++
 .../tinkerpop/gremlin/process/traversal/TextP.java |   7 +
 .../process/traversal/TraversalStrategies.java     |   2 +
 .../traversal/dsl/graph/GraphTraversal.java        |   7 +
 .../traversal/dsl/graph/GraphTraversalSource.java  |   3 +
 .../traversal/step/sideEffect/AddPropertyStep.java |   9 +
 .../ChildTraversalVerificationStrategy.java        |  90 +++++++
 .../traversal/util/ChildTraversalContext.java      |  42 ++++
 .../traversal/util/ChildTraversalValidator.java    | 111 +++++++++
 .../ChildTraversalVerificationStrategyTest.java    |  69 ++++++
 .../util/ChildTraversalValidatorTest.java          | 228 +++++++++++++++++
 .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs |  16 ++
 gremlin-go/driver/cucumber/gremlin.go              |  16 ++
 .../gremlin-javascript/test/cucumber/gremlin.js    |  16 ++
 .../src/main/python/tests/feature/gremlin.py       |  16 ++
 .../gremlin/language/translator/translations.json  | 272 +++++++++++++++++++++
 .../filter/ChildTraversalVerification.feature      | 202 +++++++++++++++
 17 files changed, 1121 insertions(+)

diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
index 24b7016ae1..80779cd56a 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
@@ -26,6 +26,7 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.util.AndP;
 import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
 import org.apache.tinkerpop.gremlin.structure.util.CloseableIterator;
 import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
+import 
org.apache.tinkerpop.gremlin.process.traversal.util.ChildTraversalValidator;
 
 import java.io.Serializable;
 import java.util.ArrayList;
@@ -725,6 +726,7 @@ public class P<V> implements Predicate<V>, Serializable, 
Cloneable {
      * @since 4.0.0
      */
     public static <V> P<V> eq(final Traversal<?, ?> traversalValue) {
+        
ChildTraversalValidator.validateFilterContext(traversalValue.asAdmin());
         return new P(Compare.eq, traversalValue.asAdmin());
     }
 
@@ -734,6 +736,7 @@ public class P<V> implements Predicate<V>, Serializable, 
Cloneable {
      * @since 4.0.0
      */
     public static <V> P<V> neq(final Traversal<?, ?> traversalValue) {
+        
ChildTraversalValidator.validateFilterContext(traversalValue.asAdmin());
         return new P(Compare.neq, traversalValue.asAdmin());
     }
 
@@ -743,6 +746,7 @@ public class P<V> implements Predicate<V>, Serializable, 
Cloneable {
      * @since 4.0.0
      */
     public static <V> P<V> lt(final Traversal<?, ?> traversalValue) {
+        
ChildTraversalValidator.validateFilterContext(traversalValue.asAdmin());
         return new P(Compare.lt, traversalValue.asAdmin());
     }
 
@@ -752,6 +756,7 @@ public class P<V> implements Predicate<V>, Serializable, 
Cloneable {
      * @since 4.0.0
      */
     public static <V> P<V> lte(final Traversal<?, ?> traversalValue) {
+        
ChildTraversalValidator.validateFilterContext(traversalValue.asAdmin());
         return new P(Compare.lte, traversalValue.asAdmin());
     }
 
@@ -761,6 +766,7 @@ public class P<V> implements Predicate<V>, Serializable, 
Cloneable {
      * @since 4.0.0
      */
     public static <V> P<V> gt(final Traversal<?, ?> traversalValue) {
+        
ChildTraversalValidator.validateFilterContext(traversalValue.asAdmin());
         return new P(Compare.gt, traversalValue.asAdmin());
     }
 
@@ -770,6 +776,7 @@ public class P<V> implements Predicate<V>, Serializable, 
Cloneable {
      * @since 4.0.0
      */
     public static <V> P<V> gte(final Traversal<?, ?> traversalValue) {
+        
ChildTraversalValidator.validateFilterContext(traversalValue.asAdmin());
         return new P(Compare.gte, traversalValue.asAdmin());
     }
 
@@ -779,6 +786,7 @@ public class P<V> implements Predicate<V>, Serializable, 
Cloneable {
      * @since 4.0.0
      */
     public static <V> P<V> within(final Traversal<?, ?> traversalValue) {
+        
ChildTraversalValidator.validateFilterContext(traversalValue.asAdmin());
         return new P(Contains.within, traversalValue.asAdmin());
     }
 
@@ -797,6 +805,9 @@ public class P<V> implements Predicate<V>, Serializable, 
Cloneable {
                 traversals.add(tv.asAdmin());
             }
         }
+        for (final Traversal.Admin<?, ?> tv : traversals) {
+            ChildTraversalValidator.validateFilterContext(tv);
+        }
         return new P(Contains.within, traversals);
     }
 
@@ -806,6 +817,7 @@ public class P<V> implements Predicate<V>, Serializable, 
Cloneable {
      * @since 4.0.0
      */
     public static <V> P<V> without(final Traversal<?, ?> traversalValue) {
+        
ChildTraversalValidator.validateFilterContext(traversalValue.asAdmin());
         return new P(Contains.without, traversalValue.asAdmin());
     }
 
@@ -824,6 +836,9 @@ public class P<V> implements Predicate<V>, Serializable, 
Cloneable {
                 traversals.add(tv.asAdmin());
             }
         }
+        for (final Traversal.Admin<?, ?> tv : traversals) {
+            ChildTraversalValidator.validateFilterContext(tv);
+        }
         return new P(Contains.without, traversals);
     }
 
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TextP.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TextP.java
index 8ec80a7c62..dcbe70a0cd 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TextP.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TextP.java
@@ -19,6 +19,7 @@
 package org.apache.tinkerpop.gremlin.process.traversal;
 
 import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import 
org.apache.tinkerpop.gremlin.process.traversal.util.ChildTraversalValidator;
 
 import java.util.Collection;
 import java.util.Map;
@@ -92,6 +93,7 @@ public class TextP extends P<String> {
      * @since 4.0.0
      */
     public static TextP startingWith(final Traversal<?, ?> traversalValue) {
+        
ChildTraversalValidator.validateFilterContext(traversalValue.asAdmin());
         return new TextP(Text.startingWith, traversalValue.asAdmin());
     }
 
@@ -119,6 +121,7 @@ public class TextP extends P<String> {
      * @since 4.0.0
      */
     public static TextP notStartingWith(final Traversal<?, ?> traversalValue) {
+        
ChildTraversalValidator.validateFilterContext(traversalValue.asAdmin());
         return new TextP(Text.notStartingWith, traversalValue.asAdmin());
     }
 
@@ -146,6 +149,7 @@ public class TextP extends P<String> {
      * @since 4.0.0
      */
     public static TextP endingWith(final Traversal<?, ?> traversalValue) {
+        
ChildTraversalValidator.validateFilterContext(traversalValue.asAdmin());
         return new TextP(Text.endingWith, traversalValue.asAdmin());
     }
 
@@ -173,6 +177,7 @@ public class TextP extends P<String> {
      * @since 4.0.0
      */
     public static TextP notEndingWith(final Traversal<?, ?> traversalValue) {
+        
ChildTraversalValidator.validateFilterContext(traversalValue.asAdmin());
         return new TextP(Text.notEndingWith, traversalValue.asAdmin());
     }
 
@@ -200,6 +205,7 @@ public class TextP extends P<String> {
      * @since 4.0.0
      */
     public static TextP containing(final Traversal<?, ?> traversalValue) {
+        
ChildTraversalValidator.validateFilterContext(traversalValue.asAdmin());
         return new TextP(Text.containing, traversalValue.asAdmin());
     }
 
@@ -227,6 +233,7 @@ public class TextP extends P<String> {
      * @since 4.0.0
      */
     public static TextP notContaining(final Traversal<?, ?> traversalValue) {
+        
ChildTraversalValidator.validateFilterContext(traversalValue.asAdmin());
         return new TextP(Text.notContaining, traversalValue.asAdmin());
     }
     
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
index bccb983665..e3f0d90ff6 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java
@@ -52,6 +52,7 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.Path
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathRetractionStrategy;
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy;
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RepeatUnrollStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ChildTraversalVerificationStrategy;
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ComputerVerificationStrategy;
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy;
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.LambdaRestrictionStrategy;
@@ -287,6 +288,7 @@ public interface TraversalStrategies extends Serializable, 
Cloneable, Iterable<T
                     LazyBarrierStrategy.instance(),
                     ProfileStrategy.instance(),
                     StandardVerificationStrategy.instance(),
+                    ChildTraversalVerificationStrategy.instance(),
                     GValueReductionStrategy.instance());
             registerStrategies(Graph.class, graphStrategies);
             registerStrategies(EmptyGraph.class, new 
DefaultTraversalStrategies());
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
index 97129c4ba0..8364cb8332 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
@@ -209,6 +209,7 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions;
 import 
org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalMetrics;
+import 
org.apache.tinkerpop.gremlin.process.traversal.util.ChildTraversalValidator;
 import org.apache.tinkerpop.gremlin.structure.Column;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 import org.apache.tinkerpop.gremlin.structure.Edge;
@@ -426,6 +427,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, 
E> {
      */
     public default GraphTraversal<S, Vertex> V(final Traversal<?, ?> 
traversal) {
         if (null == traversal) return V(new Object[]{ null });
+        ChildTraversalValidator.validateLookupContext(traversal.asAdmin());
         this.asAdmin().getGremlinLang().addStep(Symbols.V, traversal);
         return this.asAdmin().addStep(new GraphStep<>(this.asAdmin(), 
Vertex.class, false, traversal.asAdmin()));
     }
@@ -465,6 +467,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, 
E> {
      */
     public default GraphTraversal<S, Edge> E(final Traversal<?, ?> traversal) {
         if (null == traversal) return E(new Object[]{ null });
+        ChildTraversalValidator.validateLookupContext(traversal.asAdmin());
         this.asAdmin().getGremlinLang().addStep(Symbols.E, traversal);
         return this.asAdmin().addStep(new GraphStep<>(this.asAdmin(), 
Edge.class, false, traversal.asAdmin()));
     }
@@ -2715,6 +2718,7 @@ public interface GraphTraversal<S, E> extends 
Traversal<S, E> {
      */
     public default GraphTraversal<S, E> has(final String propertyKey, final 
Traversal<?, ?> traversal) {
         if (null == traversal) return has(propertyKey, (Object) null);
+        ChildTraversalValidator.validateFilterContext(traversal.asAdmin());
         this.asAdmin().getGremlinLang().addStep(Symbols.has, propertyKey, 
traversal);
         final HasContainer hasContainer = new HasContainer(propertyKey, 
traversal.asAdmin());
         return this.asAdmin().addStep(new HasStep(this.asAdmin(), 
hasContainer));
@@ -2735,6 +2739,7 @@ public interface GraphTraversal<S, E> extends 
Traversal<S, E> {
         if (null == accessor)
             throw new IllegalArgumentException("The T accessor value of 
has(T,Traversal) cannot be null");
         if (null == traversal) return has(accessor, (Object) null);
+        ChildTraversalValidator.validateFilterContext(traversal.asAdmin());
 
         this.asAdmin().getGremlinLang().addStep(Symbols.has, accessor, 
traversal);
         final HasContainer hasContainer = new 
HasContainer(accessor.getAccessor(), traversal.asAdmin());
@@ -2787,6 +2792,7 @@ public interface GraphTraversal<S, E> extends 
Traversal<S, E> {
      */
     public default GraphTraversal<S, E> has(final String label, final String 
propertyKey, final Traversal<?, ?> traversal) {
         if (null == traversal) return has(label, propertyKey, (Object) null);
+        ChildTraversalValidator.validateFilterContext(traversal.asAdmin());
         this.asAdmin().getGremlinLang().addStep(Symbols.has, label, 
propertyKey, traversal);
         TraversalHelper.addHasContainer(this.asAdmin(), new 
HasContainer(T.label.getAccessor(), P.eq(label)));
         final HasContainer hasContainer = new HasContainer(propertyKey, 
traversal.asAdmin());
@@ -3936,6 +3942,7 @@ public interface GraphTraversal<S, E> extends 
Traversal<S, E> {
      */
     public default GraphTraversal<S, E> property(final Traversal<?, ?> 
mapTraversal) {
         if (null == mapTraversal) return this;
+        
ChildTraversalValidator.validateMutationContext(mapTraversal.asAdmin());
         this.asAdmin().getGremlinLang().addStep(Symbols.property, 
mapTraversal);
         this.asAdmin().addStep(new AddPropertyStepPlaceholder(this.asAdmin(), 
null, "~traversalMap", mapTraversal.asAdmin()));
         return this;
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java
index b0991a67b9..96a9ae7980 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java
@@ -42,6 +42,7 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectStep
 import 
org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStepContract;
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.RequirementsStrategy;
 import 
org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import 
org.apache.tinkerpop.gremlin.process.traversal.util.ChildTraversalValidator;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Graph;
@@ -548,6 +549,7 @@ public class GraphTraversalSource implements 
TraversalSource {
      */
     public GraphTraversal<Vertex, Vertex> V(final Traversal<?, ?> traversal) {
         if (null == traversal) return V(new Object[]{ null });
+        ChildTraversalValidator.validateLookupContext(traversal.asAdmin());
         final GraphTraversalSource clone = this.clone();
         clone.gremlinLang.addStep(GraphTraversal.Symbols.V, traversal);
         final GraphTraversal.Admin<Vertex, Vertex> traversalAdmin = new 
DefaultGraphTraversal<>(clone);
@@ -586,6 +588,7 @@ public class GraphTraversalSource implements 
TraversalSource {
      */
     public GraphTraversal<Edge, Edge> E(final Traversal<?, ?> traversal) {
         if (null == traversal) return E(new Object[]{ null });
+        ChildTraversalValidator.validateLookupContext(traversal.asAdmin());
         final GraphTraversalSource clone = this.clone();
         clone.gremlinLang.addStep(GraphTraversal.Symbols.E, traversal);
         final GraphTraversal.Admin<Edge, Edge> traversalAdmin = new 
DefaultGraphTraversal<>(clone);
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
index bc83ca4b0c..0c7f30c919 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
@@ -148,6 +148,15 @@ public class AddPropertyStep<S extends Element> extends 
SideEffectStep<S> implem
             return;
         }
 
+        // Runtime validation for property(traversal) — the Map-producing form 
uses sentinel key "~traversalMap".
+        // If the traversal did NOT produce a Map, reject it to prevent the 
sentinel key from leaking as a real property.
+        if ("~traversalMap".equals(key)) {
+            final Object result = results.get(0);
+            throw new IllegalArgumentException(
+                    "property(traversal) requires the traversal to produce a 
Map, but got: " +
+                    (result == null ? "null" : 
result.getClass().getSimpleName()));
+        }
+
         // Multi-result handling with cardinality awareness
         if (results.size() > 1) {
             // Determine effective cardinality
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/ChildTraversalVerificationStrategy.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/ChildTraversalVerificationStrategy.java
new file mode 100644
index 0000000000..95ba0cc642
--- /dev/null
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/ChildTraversalVerificationStrategy.java
@@ -0,0 +1,90 @@
+/*
+ * 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.tinkerpop.gremlin.process.traversal.strategy.verification;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.AllStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.AnyStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.HasStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.IsStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.NoneStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.AddPropertyStepContract;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.util.ChildTraversalContext;
+import 
org.apache.tinkerpop.gremlin.process.traversal.util.ChildTraversalValidator;
+
+/**
+ * A verification strategy that validates child traversals do not contain 
disallowed mutating steps
+ * based on the context in which they are used. This serves as a safety net 
for cases where
+ * construction-time validation is bypassed (e.g., programmatic traversal 
construction).
+ * <p>
+ * Context classification:
+ * <ul>
+ *   <li>{@code HasStep}, {@code IsStep}, {@code AllStep}, {@code AnyStep}, 
{@code NoneStep} → FILTER</li>
+ *   <li>{@code GraphStep} with idTraversal → LOOKUP</li>
+ *   <li>{@code AddPropertyStepContract} → MUTATION</li>
+ * </ul>
+ */
+public final class ChildTraversalVerificationStrategy
+        extends 
AbstractTraversalStrategy<TraversalStrategy.VerificationStrategy>
+        implements TraversalStrategy.VerificationStrategy {
+
+    private static final ChildTraversalVerificationStrategy INSTANCE = new 
ChildTraversalVerificationStrategy();
+
+    private ChildTraversalVerificationStrategy() {
+    }
+
+    @Override
+    public void apply(final Traversal.Admin<?, ?> traversal) {
+        for (final Step<?, ?> step : traversal.getSteps()) {
+            if (step instanceof TraversalParent) {
+                final ChildTraversalContext context = classifyContext(step);
+                if (context != ChildTraversalContext.NONE) {
+                    for (final Traversal.Admin<?, ?> child : 
((TraversalParent) step).getLocalChildren()) {
+                        try {
+                            ChildTraversalValidator.validateRecursive(child, 
context);
+                        } catch (final IllegalArgumentException e) {
+                            throw new VerificationException(e.getMessage(), 
traversal);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private ChildTraversalContext classifyContext(final Step<?, ?> step) {
+        if (step instanceof HasStep || step instanceof IsStep ||
+                step instanceof AllStep || step instanceof AnyStep || step 
instanceof NoneStep) {
+            return ChildTraversalContext.FILTER;
+        } else if (step instanceof GraphStep && ((GraphStep<?, ?>) 
step).getIdTraversal() != null) {
+            return ChildTraversalContext.LOOKUP;
+        } else if (step instanceof AddPropertyStepContract) {
+            return ChildTraversalContext.MUTATION;
+        }
+        return ChildTraversalContext.NONE;
+    }
+
+    public static ChildTraversalVerificationStrategy instance() {
+        return INSTANCE;
+    }
+}
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/ChildTraversalContext.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/ChildTraversalContext.java
new file mode 100644
index 0000000000..fbb489813f
--- /dev/null
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/ChildTraversalContext.java
@@ -0,0 +1,42 @@
+/*
+ * 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.tinkerpop.gremlin.process.traversal.util;
+
+/**
+ * Classifies the context in which a child traversal is used, determining what 
validation rules apply.
+ *
+ * <ul>
+ *   <li>{@link #FILTER} — child traversals inside {@code has()}, {@code 
is()}, {@code all()}, {@code any()},
+ *       {@code none()}, or {@code choose()} predicates. No {@link 
org.apache.tinkerpop.gremlin.process.traversal.step.Mutating}
+ *       steps are allowed.</li>
+ *   <li>{@link #LOOKUP} — child traversals inside {@code V(traversal)} or 
{@code E(traversal)}. No
+ *       {@link org.apache.tinkerpop.gremlin.process.traversal.step.Mutating} 
steps are allowed.</li>
+ *   <li>{@link #MUTATION} — child traversals inside {@code 
property(traversal)}. Only
+ *       {@link 
org.apache.tinkerpop.gremlin.process.traversal.step.filter.DropStep} is 
blocked; other mutating
+ *       steps are permitted because the traversal intentionally produces 
values from graph state.</li>
+ *   <li>{@link #NONE} — not a child-traversal-bearing step, or a step whose 
children are not validated
+ *       (e.g., {@code mergeV}, {@code mergeE}).</li>
+ * </ul>
+ */
+public enum ChildTraversalContext {
+    FILTER,
+    LOOKUP,
+    MUTATION,
+    NONE
+}
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/ChildTraversalValidator.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/ChildTraversalValidator.java
new file mode 100644
index 0000000000..95c422eabe
--- /dev/null
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/ChildTraversalValidator.java
@@ -0,0 +1,111 @@
+/*
+ * 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.tinkerpop.gremlin.process.traversal.util;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.DropStep;
+
+/**
+ * Validates child traversals to ensure they do not contain disallowed steps 
based on the context
+ * in which they are used. This utility is shared between construction-time 
validation (in API methods)
+ * and strategy-time validation ({@code ChildTraversalVerificationStrategy}).
+ * <p>
+ * Validation rules:
+ * <ul>
+ *   <li><b>FILTER / LOOKUP context:</b> No steps implementing {@link 
Mutating} are allowed at any nesting depth.</li>
+ *   <li><b>MUTATION context:</b> Only {@link DropStep} is blocked; other 
mutating steps are permitted.</li>
+ * </ul>
+ * <p>
+ * Recursion walks both {@link TraversalParent#getLocalChildren()} and
+ * {@link TraversalParent#getGlobalChildren()} to detect mutations nested 
inside
+ * {@code map()}, {@code union()}, {@code choose()}, {@code coalesce()}, or 
any other parent step.
+ */
+public final class ChildTraversalValidator {
+
+    private ChildTraversalValidator() {
+        // static utility
+    }
+
+    /**
+     * Validates a child traversal used in filter context (has, is, all, any, 
none, choose predicate).
+     * Throws {@link IllegalArgumentException} if any {@link Mutating} step is 
found.
+     */
+    public static void validateFilterContext(final Traversal.Admin<?, ?> 
child) {
+        validateRecursive(child, ChildTraversalContext.FILTER);
+    }
+
+    /**
+     * Validates a child traversal used in lookup context (V(traversal), 
E(traversal)).
+     * Throws {@link IllegalArgumentException} if any {@link Mutating} step is 
found.
+     */
+    public static void validateLookupContext(final Traversal.Admin<?, ?> 
child) {
+        validateRecursive(child, ChildTraversalContext.LOOKUP);
+    }
+
+    /**
+     * Validates a child traversal used in mutation context 
(property(traversal)).
+     * Throws {@link IllegalArgumentException} if a {@link DropStep} is found.
+     */
+    public static void validateMutationContext(final Traversal.Admin<?, ?> 
child) {
+        validateRecursive(child, ChildTraversalContext.MUTATION);
+    }
+
+    /**
+     * Recursively validates all steps in the child traversal and its nested 
children.
+     */
+    public static void validateRecursive(final Traversal.Admin<?, ?> child,
+                                  final ChildTraversalContext context) {
+        for (final Step<?, ?> step : child.getSteps()) {
+            validateStep(step, context);
+            if (step instanceof TraversalParent) {
+                for (final Traversal.Admin<?, ?> nested : ((TraversalParent) 
step).getLocalChildren()) {
+                    validateRecursive(nested, context);
+                }
+                for (final Traversal.Admin<?, ?> nested : ((TraversalParent) 
step).getGlobalChildren()) {
+                    validateRecursive(nested, context);
+                }
+            }
+        }
+    }
+
+    private static void validateStep(final Step<?, ?> step, final 
ChildTraversalContext context) {
+        switch (context) {
+            case FILTER:
+            case LOOKUP:
+                if (step instanceof Mutating) {
+                    throw new IllegalArgumentException(
+                            "Child traversal in " + 
context.name().toLowerCase() + " context contains mutating step " +
+                            step.getClass().getSimpleName() + ". Mutating 
steps are not allowed in this context.");
+                }
+                break;
+            case MUTATION:
+                if (step instanceof DropStep) {
+                    throw new IllegalArgumentException(
+                            "Child traversal in mutation context contains 
DropStep. " +
+                            "Destructive steps are not allowed inside 
property(traversal).");
+                }
+                break;
+            default:
+                break;
+        }
+    }
+}
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/ChildTraversalVerificationStrategyTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/ChildTraversalVerificationStrategyTest.java
new file mode 100644
index 0000000000..feb0688283
--- /dev/null
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/ChildTraversalVerificationStrategyTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.tinkerpop.gremlin.process.traversal.strategy.verification;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
+import 
org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import org.junit.Test;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+/**
+ * Tests for the ChildTraversalVerificationStrategy (strategy-time layer).
+ */
+public class ChildTraversalVerificationStrategyTest {
+
+    private final GraphTraversalSource g = EmptyGraph.instance().traversal();
+
+    @Test
+    public void shouldBeRegisteredByDefault() {
+        // The strategy should be present in the default Graph strategy set 
(not EmptyGraph which has none)
+        assertTrue(TraversalStrategies.GlobalCache
+                .getStrategies(Graph.class)
+                
.getStrategy(ChildTraversalVerificationStrategy.class).isPresent());
+    }
+
+    @Test
+    public void shouldBeRemovableViaWithoutStrategies() {
+        // Users should be able to remove the strategy if needed
+        final GraphTraversalSource gNoVerify = 
g.withoutStrategies(ChildTraversalVerificationStrategy.class);
+        
assertFalse(gNoVerify.getStrategies().getStrategy(ChildTraversalVerificationStrategy.class).isPresent());
+    }
+
+    @Test
+    public void shouldNotRejectValidReadOnlyTraversal() {
+        // Valid traversal should pass strategy application without error
+        final Traversal.Admin<?, ?> traversal = g.V().has("name", 
__.V().values("name")).asAdmin();
+        traversal.applyStrategies();
+        // If we get here without exception, the test passes
+    }
+
+    @Test
+    public void shouldNotRejectValidLookupTraversal() {
+        // Valid V(traversal) should pass
+        final Traversal.Admin<?, ?> traversal = 
g.V().V(__.out("knows").id()).asAdmin();
+        traversal.applyStrategies();
+    }
+}
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/util/ChildTraversalValidatorTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/util/ChildTraversalValidatorTest.java
new file mode 100644
index 0000000000..e3b385dd48
--- /dev/null
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/util/ChildTraversalValidatorTest.java
@@ -0,0 +1,228 @@
+/*
+ * 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.tinkerpop.gremlin.process.traversal.util;
+
+import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.TextP;
+import 
org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import org.junit.Test;
+
+import static org.junit.Assert.assertThrows;
+
+/**
+ * Tests for construction-time child traversal validation.
+ */
+public class ChildTraversalValidatorTest {
+
+    private final GraphTraversalSource g = EmptyGraph.instance().traversal();
+
+    // ===== FILTER CONTEXT: should reject mutating steps =====
+
+    @Test
+    public void shouldRejectAddVInHasTraversal() {
+        assertThrows(IllegalArgumentException.class, () ->
+                g.V().has("name", __.addV("x").values("name")));
+    }
+
+    @Test
+    public void shouldRejectDropInHasTraversal() {
+        assertThrows(IllegalArgumentException.class, () ->
+                g.V().has("name", __.V().drop().constant("x")));
+    }
+
+    @Test
+    public void shouldRejectNestedMutatingInHasTraversal() {
+        assertThrows(IllegalArgumentException.class, () ->
+                g.V().has("name", __.V().map(__.addV("x")).values("name")));
+    }
+
+    @Test
+    public void shouldRejectMutatingInHasWithTAccessor() {
+        assertThrows(IllegalArgumentException.class, () ->
+                g.V().has(org.apache.tinkerpop.gremlin.structure.T.label, 
__.addV("x").label()));
+    }
+
+    @Test
+    public void shouldRejectMutatingInHasWithLabel() {
+        assertThrows(IllegalArgumentException.class, () ->
+                g.V().has("person", "name", __.addV("x").values("name")));
+    }
+
+    // ===== LOOKUP CONTEXT: should reject mutating steps =====
+
+    @Test
+    public void shouldRejectAddVInMidTraversalV() {
+        assertThrows(IllegalArgumentException.class, () ->
+                g.V().V(__.addV("x").id()));
+    }
+
+    @Test
+    public void shouldRejectAddVInMidTraversalE() {
+        assertThrows(IllegalArgumentException.class, () ->
+                g.V().E(__.addV("x").id()));
+    }
+
+    @Test
+    public void shouldRejectAddVInStartStepV() {
+        assertThrows(IllegalArgumentException.class, () ->
+                g.V(__.addV("x").id()));
+    }
+
+    @Test
+    public void shouldRejectAddVInStartStepE() {
+        assertThrows(IllegalArgumentException.class, () ->
+                g.E(__.addV("x").id()));
+    }
+
+    // ===== P FACTORY METHODS: should reject mutating steps =====
+
+    @Test
+    public void shouldRejectMutatingInPEq() {
+        assertThrows(IllegalArgumentException.class, () ->
+                P.eq(__.addV("x").values("name")));
+    }
+
+    @Test
+    public void shouldRejectMutatingInPNeq() {
+        assertThrows(IllegalArgumentException.class, () ->
+                P.neq(__.addV("x").values("name")));
+    }
+
+    @Test
+    public void shouldRejectMutatingInPGt() {
+        assertThrows(IllegalArgumentException.class, () ->
+                P.gt(__.addV("x").values("age")));
+    }
+
+    @Test
+    public void shouldRejectMutatingInPLt() {
+        assertThrows(IllegalArgumentException.class, () ->
+                P.lt(__.addV("x").values("age")));
+    }
+
+    @Test
+    public void shouldRejectMutatingInPGte() {
+        assertThrows(IllegalArgumentException.class, () ->
+                P.gte(__.addV("x").values("age")));
+    }
+
+    @Test
+    public void shouldRejectMutatingInPLte() {
+        assertThrows(IllegalArgumentException.class, () ->
+                P.lte(__.addV("x").values("age")));
+    }
+
+    @Test
+    public void shouldRejectMutatingInPWithin() {
+        assertThrows(IllegalArgumentException.class, () ->
+                P.within(__.addV("x").values("name")));
+    }
+
+    @Test
+    public void shouldRejectMutatingInPWithout() {
+        assertThrows(IllegalArgumentException.class, () ->
+                P.without(__.addV("x").values("name")));
+    }
+
+    @Test
+    public void shouldRejectMutatingInMultiTraversalWithin() {
+        assertThrows(IllegalArgumentException.class, () ->
+                P.within(__.V().values("name"), __.addV("x").values("name")));
+    }
+
+    @Test
+    public void shouldRejectMutatingInMultiTraversalWithout() {
+        assertThrows(IllegalArgumentException.class, () ->
+                P.without(__.V().values("name"), __.addV("x").values("name")));
+    }
+
+    // ===== TextP FACTORY METHODS: should reject mutating steps =====
+
+    @Test
+    public void shouldRejectMutatingInTextPStartingWith() {
+        assertThrows(IllegalArgumentException.class, () ->
+                TextP.startingWith(__.addV("x").values("name")));
+    }
+
+    @Test
+    public void shouldRejectMutatingInTextPContaining() {
+        assertThrows(IllegalArgumentException.class, () ->
+                TextP.containing(__.addV("x").values("name")));
+    }
+
+    // ===== MUTATION CONTEXT: should reject DropStep but allow other 
mutations =====
+
+    @Test
+    public void shouldRejectDropInPropertyTraversal() {
+        assertThrows(IllegalArgumentException.class, () ->
+                g.V().property(__.V().map(__.drop()).project("x").by("name")));
+    }
+
+    @Test
+    public void shouldRejectNestedDropInPropertyTraversal() {
+        assertThrows(IllegalArgumentException.class, () ->
+                g.V().property(__.V().union(__.drop(), 
__.constant("x")).project("k").by()));
+    }
+
+    // ===== VALID TRAVERSALS: should pass without error =====
+
+    @Test
+    public void shouldAllowReadOnlyHasTraversal() {
+        // Should not throw
+        g.V().has("name", __.V().values("name"));
+    }
+
+    @Test
+    public void shouldAllowNavigationInHasTraversal() {
+        // Should not throw
+        g.V().has("name", __.V().out("knows").values("name"));
+    }
+
+    @Test
+    public void shouldAllowReadOnlyLookupTraversal() {
+        // Should not throw
+        g.V().V(__.out("knows").id());
+    }
+
+    @Test
+    public void shouldAllowReadOnlyStartStepV() {
+        // Should not throw
+        g.V(__.V().id());
+    }
+
+    @Test
+    public void shouldAllowReadOnlyPredicate() {
+        // Should not throw
+        P.eq(__.V().values("age"));
+    }
+
+    @Test
+    public void shouldAllowAddVInMutationContext() {
+        // addV is allowed in property(traversal) — only DropStep is blocked
+        g.V().property(__.V().addV("temp").project("k").by("name"));
+    }
+
+    @Test
+    public void shouldAllowReadOnlyPropertyTraversal() {
+        // Should not throw
+        g.V().property(__.V().project("name").by("name"));
+    }
+}
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs 
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index e349b0f622..d460dcadeb 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -373,6 +373,22 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_injectX7X_anyXeqX7XX", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {(g,p) 
=>g.Inject<object>(7).Any(P.Eq(7))}}, 
                {"g_injectXnull_nullX_anyXeqXnullXX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Inject<object>(new List<object> { null, null }).Any(P.Eq(null))}}, 
                {"g_injectX3_threeX_anyXeqX3XX", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.Inject<object>(new List<object> { 3, "three" }).Any(P.Eq(3))}}, 
+               {"g_V_hasXname_addVXxX_valuesXnameXX_rejected", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().Has("name", __.AddV((string) "x").Values<object>("name"))}}, 
+               {"g_V_hasXname_V_drop_constantXxXX_rejected", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().Has("name", __.V().Drop().Constant<object>("x"))}}, 
+               {"g_V_hasXname_V_mapXaddVXxXX_valuesXnameXX_rejected", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().Has("name", __.V().Map<object>(__.AddV((string) 
"x")).Values<object>("name"))}}, 
+               {"g_V_hasXname_eqXaddVXxX_valuesXnameXXX_rejected", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().Has("name", P.Eq(__.AddV((string) 
"x").Values<object>("name")))}}, 
+               {"g_V_hasXname_withinXaddVXxX_valuesXnameXXX_rejected", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().Has("name", P.Within(__.AddV((string) 
"x").Values<object>("name")))}}, 
+               {"g_V_hasXage_gtXaddVXxX_valuesXageXXX_rejected", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().Has("age", P.Gt(__.AddV((string) "x").Values<object>("age")))}}, 
+               {"g_V_valuesXageX_isXaddVXxX_valuesXageXX_rejected", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().Values<object>("age").Is(__.AddV((string) 
"x").Values<object>("age"))}}, 
+               {"g_V_valuesXageX_isXgtXaddVXxX_valuesXageXXX_rejected", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().Values<object>("age").Is(P.Gt(__.AddV((string) 
"x").Values<object>("age")))}}, 
+               {"g_V_VXaddVXxX_idX_rejected", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().V(__.AddV((string) "x").Id())}}, 
+               {"g_V_EXaddVXxX_idX_rejected", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().E(__.AddV((string) "x").Id())}}, 
+               {"g_VXaddVXxX_idX_rejected", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V(__.AddV((string) "x").Id())}}, 
+               {"g_EXaddVXxX_idX_rejected", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.E(__.AddV((string) "x").Id())}}, 
+               {"g_V_propertyXV_mapXdropX_projectXxX_byXnameXX_rejected", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().Property((ITraversal) 
__.V().Map<object>(__.Drop()).Project<object>("x").By("name"))}}, 
+               {"g_V_hasXname_VXvid1X_valuesXnameXX_passes_verification", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().Has("name", __.V(p["vid1"]).Values<object>("name"))}}, 
+               {"g_V_hasXage_gtXVXvid1X_valuesXageXXX_passes_verification", 
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().Has("age", P.Gt(__.V(p["vid1"]).Values<object>("age")))}}, 
+               {"g_V_VXoutXknowsX_idX_valuesXnameX_passes_verification", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V(p["vid1"]).V(__.Out("knows").Id()).Values<object>("name")}}, 
                {"g_V_coinX1_0X", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Coin(1.0)}}, 
                {"g_V_coinX1X", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Coin(1)}}, 
                {"g_V_coinX0X", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Coin(0.0)}}, 
diff --git a/gremlin-go/driver/cucumber/gremlin.go 
b/gremlin-go/driver/cucumber/gremlin.go
index 1ff3d19a48..60808e97d4 100644
--- a/gremlin-go/driver/cucumber/gremlin.go
+++ b/gremlin-go/driver/cucumber/gremlin.go
@@ -343,6 +343,22 @@ var translationMap = map[string][]func(g 
*gremlingo.GraphTraversalSource, p map[
     "g_injectX7X_anyXeqX7XX": {func(g *gremlingo.GraphTraversalSource, p 
map[string]interface{}) *gremlingo.GraphTraversal {return 
g.Inject(7).Any(gremlingo.P.Eq(7))}}, 
     "g_injectXnull_nullX_anyXeqXnullXX": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return g.Inject([]interface{}{nil, 
nil}).Any(gremlingo.P.Eq(nil))}}, 
     "g_injectX3_threeX_anyXeqX3XX": {func(g *gremlingo.GraphTraversalSource, p 
map[string]interface{}) *gremlingo.GraphTraversal {return 
g.Inject([]interface{}{3, "three"}).Any(gremlingo.P.Eq(3))}}, 
+    "g_V_hasXname_addVXxX_valuesXnameXX_rejected": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return g.V().Has("name", 
gremlingo.T__.AddV("x").Values("name"))}}, 
+    "g_V_hasXname_V_drop_constantXxXX_rejected": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return g.V().Has("name", 
gremlingo.T__.V().Drop().Constant("x"))}}, 
+    "g_V_hasXname_V_mapXaddVXxXX_valuesXnameXX_rejected": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return g.V().Has("name", 
gremlingo.T__.V().Map(gremlingo.T__.AddV("x")).Values("name"))}}, 
+    "g_V_hasXname_eqXaddVXxX_valuesXnameXXX_rejected": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return g.V().Has("name", 
gremlingo.P.Eq(gremlingo.T__.AddV("x").Values("name")))}}, 
+    "g_V_hasXname_withinXaddVXxX_valuesXnameXXX_rejected": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return g.V().Has("name", 
gremlingo.P.Within(gremlingo.T__.AddV("x").Values("name")))}}, 
+    "g_V_hasXage_gtXaddVXxX_valuesXageXXX_rejected": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return g.V().Has("age", 
gremlingo.P.Gt(gremlingo.T__.AddV("x").Values("age")))}}, 
+    "g_V_valuesXageX_isXaddVXxX_valuesXageXX_rejected": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V().Values("age").Is(gremlingo.T__.AddV("x").Values("age"))}}, 
+    "g_V_valuesXageX_isXgtXaddVXxX_valuesXageXXX_rejected": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V().Values("age").Is(gremlingo.P.Gt(gremlingo.T__.AddV("x").Values("age")))}},
 
+    "g_V_VXaddVXxX_idX_rejected": {func(g *gremlingo.GraphTraversalSource, p 
map[string]interface{}) *gremlingo.GraphTraversal {return 
g.V().V(gremlingo.T__.AddV("x").Id())}}, 
+    "g_V_EXaddVXxX_idX_rejected": {func(g *gremlingo.GraphTraversalSource, p 
map[string]interface{}) *gremlingo.GraphTraversal {return 
g.V().E(gremlingo.T__.AddV("x").Id())}}, 
+    "g_VXaddVXxX_idX_rejected": {func(g *gremlingo.GraphTraversalSource, p 
map[string]interface{}) *gremlingo.GraphTraversal {return 
g.V(gremlingo.T__.AddV("x").Id())}}, 
+    "g_EXaddVXxX_idX_rejected": {func(g *gremlingo.GraphTraversalSource, p 
map[string]interface{}) *gremlingo.GraphTraversal {return 
g.E(gremlingo.T__.AddV("x").Id())}}, 
+    "g_V_propertyXV_mapXdropX_projectXxX_byXnameXX_rejected": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V().Property(gremlingo.T__.V().Map(gremlingo.T__.Drop()).Project("x").By("name"))}},
 
+    "g_V_hasXname_VXvid1X_valuesXnameXX_passes_verification": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return g.V().Has("name", 
gremlingo.T__.V(p["vid1"]).Values("name"))}}, 
+    "g_V_hasXage_gtXVXvid1X_valuesXageXXX_passes_verification": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return g.V().Has("age", 
gremlingo.P.Gt(gremlingo.T__.V(p["vid1"]).Values("age")))}}, 
+    "g_V_VXoutXknowsX_idX_valuesXnameX_passes_verification": {func(g 
*gremlingo.GraphTraversalSource, p map[string]interface{}) 
*gremlingo.GraphTraversal {return 
g.V(p["vid1"]).V(gremlingo.T__.Out("knows").Id()).Values("name")}}, 
     "g_V_coinX1_0X": {func(g *gremlingo.GraphTraversalSource, p 
map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Coin(1.0)}}, 
     "g_V_coinX1X": {func(g *gremlingo.GraphTraversalSource, p 
map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Coin(1)}}, 
     "g_V_coinX0X": {func(g *gremlingo.GraphTraversalSource, p 
map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Coin(0.0)}}, 
diff --git a/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js 
b/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js
index 3dc2da180a..c2a1640ae4 100644
--- a/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js
+++ b/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js
@@ -374,6 +374,22 @@ const gremlins = {
     g_injectX7X_anyXeqX7XX: [function({g}) { return g.inject(7).any(P.eq(7)) 
}], 
     g_injectXnull_nullX_anyXeqXnullXX: [function({g}) { return g.inject([null, 
null]).any(P.eq(null)) }], 
     g_injectX3_threeX_anyXeqX3XX: [function({g}) { return g.inject([3, 
"three"]).any(P.eq(3)) }], 
+    g_V_hasXname_addVXxX_valuesXnameXX_rejected: [function({g}) { return 
g.V().has("name", __.addV("x").values("name")) }], 
+    g_V_hasXname_V_drop_constantXxXX_rejected: [function({g}) { return 
g.V().has("name", __.V().drop().constant("x")) }], 
+    g_V_hasXname_V_mapXaddVXxXX_valuesXnameXX_rejected: [function({g}) { 
return g.V().has("name", __.V().map(__.addV("x")).values("name")) }], 
+    g_V_hasXname_eqXaddVXxX_valuesXnameXXX_rejected: [function({g}) { return 
g.V().has("name", P.eq(__.addV("x").values("name"))) }], 
+    g_V_hasXname_withinXaddVXxX_valuesXnameXXX_rejected: [function({g}) { 
return g.V().has("name", P.within(__.addV("x").values("name"))) }], 
+    g_V_hasXage_gtXaddVXxX_valuesXageXXX_rejected: [function({g}) { return 
g.V().has("age", P.gt(__.addV("x").values("age"))) }], 
+    g_V_valuesXageX_isXaddVXxX_valuesXageXX_rejected: [function({g}) { return 
g.V().values("age").is(__.addV("x").values("age")) }], 
+    g_V_valuesXageX_isXgtXaddVXxX_valuesXageXXX_rejected: [function({g}) { 
return g.V().values("age").is(P.gt(__.addV("x").values("age"))) }], 
+    g_V_VXaddVXxX_idX_rejected: [function({g}) { return 
g.V().V(__.addV("x").id()) }], 
+    g_V_EXaddVXxX_idX_rejected: [function({g}) { return 
g.V().E(__.addV("x").id()) }], 
+    g_VXaddVXxX_idX_rejected: [function({g}) { return g.V(__.addV("x").id()) 
}], 
+    g_EXaddVXxX_idX_rejected: [function({g}) { return g.E(__.addV("x").id()) 
}], 
+    g_V_propertyXV_mapXdropX_projectXxX_byXnameXX_rejected: [function({g}) { 
return g.V().property(__.V().map(__.drop()).project("x").by("name")) }], 
+    g_V_hasXname_VXvid1X_valuesXnameXX_passes_verification: [function({g, 
vid1}) { return g.V().has("name", __.V(vid1).values("name")) }], 
+    g_V_hasXage_gtXVXvid1X_valuesXageXXX_passes_verification: [function({g, 
vid1}) { return g.V().has("age", P.gt(__.V(vid1).values("age"))) }], 
+    g_V_VXoutXknowsX_idX_valuesXnameX_passes_verification: [function({g, 
vid1}) { return g.V(vid1).V(__.out("knows").id()).values("name") }], 
     g_V_coinX1_0X: [function({g}) { return g.V().coin(1.0) }], 
     g_V_coinX1X: [function({g}) { return g.V().coin(1) }], 
     g_V_coinX0X: [function({g}) { return g.V().coin(0.0) }], 
diff --git a/gremlin-python/src/main/python/tests/feature/gremlin.py 
b/gremlin-python/src/main/python/tests/feature/gremlin.py
index c5b2e09b1e..aa4358cfe7 100644
--- a/gremlin-python/src/main/python/tests/feature/gremlin.py
+++ b/gremlin-python/src/main/python/tests/feature/gremlin.py
@@ -348,6 +348,22 @@ world.gremlins = {
     'g_injectX7X_anyXeqX7XX': [(lambda g:g.inject(7).any_(P.eq(7)))], 
     'g_injectXnull_nullX_anyXeqXnullXX': [(lambda g:g.inject([None, 
None]).any_(P.eq(None)))], 
     'g_injectX3_threeX_anyXeqX3XX': [(lambda g:g.inject([3, 
'three']).any_(P.eq(3)))], 
+    'g_V_hasXname_addVXxX_valuesXnameXX_rejected': [(lambda 
g:g.V().has('name', __.add_v('x').values('name')))], 
+    'g_V_hasXname_V_drop_constantXxXX_rejected': [(lambda g:g.V().has('name', 
__.V().drop().constant('x')))], 
+    'g_V_hasXname_V_mapXaddVXxXX_valuesXnameXX_rejected': [(lambda 
g:g.V().has('name', __.V().map(__.add_v('x')).values('name')))], 
+    'g_V_hasXname_eqXaddVXxX_valuesXnameXXX_rejected': [(lambda 
g:g.V().has('name', P.eq(__.add_v('x').values('name'))))], 
+    'g_V_hasXname_withinXaddVXxX_valuesXnameXXX_rejected': [(lambda 
g:g.V().has('name', P.within(__.add_v('x').values('name'))))], 
+    'g_V_hasXage_gtXaddVXxX_valuesXageXXX_rejected': [(lambda 
g:g.V().has('age', P.gt(__.add_v('x').values('age'))))], 
+    'g_V_valuesXageX_isXaddVXxX_valuesXageXX_rejected': [(lambda 
g:g.V().values('age').is_(__.add_v('x').values('age')))], 
+    'g_V_valuesXageX_isXgtXaddVXxX_valuesXageXXX_rejected': [(lambda 
g:g.V().values('age').is_(P.gt(__.add_v('x').values('age'))))], 
+    'g_V_VXaddVXxX_idX_rejected': [(lambda g:g.V().V(__.add_v('x').id_()))], 
+    'g_V_EXaddVXxX_idX_rejected': [(lambda g:g.V().E(__.add_v('x').id_()))], 
+    'g_VXaddVXxX_idX_rejected': [(lambda g:g.V(__.add_v('x').id_()))], 
+    'g_EXaddVXxX_idX_rejected': [(lambda g:g.E(__.add_v('x').id_()))], 
+    'g_V_propertyXV_mapXdropX_projectXxX_byXnameXX_rejected': [(lambda 
g:g.V().property(__.V().map(__.drop()).project('x').by('name')))], 
+    'g_V_hasXname_VXvid1X_valuesXnameXX_passes_verification': [(lambda g, 
vid1=None:g.V().has('name', __.V(vid1).values('name')))], 
+    'g_V_hasXage_gtXVXvid1X_valuesXageXXX_passes_verification': [(lambda g, 
vid1=None:g.V().has('age', P.gt(__.V(vid1).values('age'))))], 
+    'g_V_VXoutXknowsX_idX_valuesXnameX_passes_verification': [(lambda g, 
vid1=None:g.V(vid1).V(__.out('knows').id_()).values('name'))], 
     'g_V_coinX1_0X': [(lambda g:g.V().coin(1.0))], 
     'g_V_coinX1X': [(lambda g:g.V().coin(1))], 
     'g_V_coinX0X': [(lambda g:g.V().coin(0.0))], 
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json
index b19cdfa20f..99148d3a53 100644
--- 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json
@@ -6295,6 +6295,278 @@
             }
         ]
     },
+    {
+        "scenario": "g_V_hasXname_addVXxX_valuesXnameXX_rejected",
+        "traversals": [
+            {
+                "original": "g.V().has(\"name\", 
__.addV(\"x\").values(\"name\"))",
+                "language": "g.V().has(\"name\", 
__.addV(\"x\").values(\"name\"))",
+                "canonical": "g.V().has(\"name\", 
__.addV(\"x\").values(\"name\"))",
+                "anonymized": "g.V().has(string0, 
__.addV(string1).values(string0))",
+                "dotnet": "g.V().Has(\"name\", __.AddV((string) 
\"x\").Values<object>(\"name\"))",
+                "go": "g.V().Has(\"name\", 
gremlingo.T__.AddV(\"x\").Values(\"name\"))",
+                "groovy": "g.V().has(\"name\", 
__.addV(\"x\").values(\"name\"))",
+                "java": "g.V().has(\"name\", __.addV(\"x\").values(\"name\"))",
+                "javascript": "g.V().has(\"name\", 
__.addV(\"x\").values(\"name\"))",
+                "python": "g.V().has('name', __.add_v('x').values('name'))"
+            }
+        ]
+    },
+    {
+        "scenario": "g_V_hasXname_V_drop_constantXxXX_rejected",
+        "traversals": [
+            {
+                "original": "g.V().has(\"name\", 
__.V().drop().constant(\"x\"))",
+                "language": "g.V().has(\"name\", 
__.V().drop().constant(\"x\"))",
+                "canonical": "g.V().has(\"name\", 
__.V().drop().constant(\"x\"))",
+                "anonymized": "g.V().has(string0, 
__.V().drop().constant(string1))",
+                "dotnet": "g.V().Has(\"name\", 
__.V().Drop().Constant<object>(\"x\"))",
+                "go": "g.V().Has(\"name\", 
gremlingo.T__.V().Drop().Constant(\"x\"))",
+                "groovy": "g.V().has(\"name\", __.V().drop().constant(\"x\"))",
+                "java": "g.V().has(\"name\", __.V().drop().constant(\"x\"))",
+                "javascript": "g.V().has(\"name\", 
__.V().drop().constant(\"x\"))",
+                "python": "g.V().has('name', __.V().drop().constant('x'))"
+            }
+        ]
+    },
+    {
+        "scenario": "g_V_hasXname_V_mapXaddVXxXX_valuesXnameXX_rejected",
+        "traversals": [
+            {
+                "original": "g.V().has(\"name\", 
__.V().map(__.addV(\"x\")).values(\"name\"))",
+                "language": "g.V().has(\"name\", 
__.V().map(__.addV(\"x\")).values(\"name\"))",
+                "canonical": "g.V().has(\"name\", 
__.V().map(__.addV(\"x\")).values(\"name\"))",
+                "anonymized": "g.V().has(string0, 
__.V().map(__.addV(string1)).values(string0))",
+                "dotnet": "g.V().Has(\"name\", 
__.V().Map<object>(__.AddV((string) \"x\")).Values<object>(\"name\"))",
+                "go": "g.V().Has(\"name\", 
gremlingo.T__.V().Map(gremlingo.T__.AddV(\"x\")).Values(\"name\"))",
+                "groovy": "g.V().has(\"name\", 
__.V().map(__.addV(\"x\")).values(\"name\"))",
+                "java": "g.V().has(\"name\", 
__.V().map(__.addV(\"x\")).values(\"name\"))",
+                "javascript": "g.V().has(\"name\", 
__.V().map(__.addV(\"x\")).values(\"name\"))",
+                "python": "g.V().has('name', 
__.V().map(__.add_v('x')).values('name'))"
+            }
+        ]
+    },
+    {
+        "scenario": "g_V_hasXname_eqXaddVXxX_valuesXnameXXX_rejected",
+        "traversals": [
+            {
+                "original": "g.V().has(\"name\", 
P.eq(__.addV(\"x\").values(\"name\")))",
+                "language": "g.V().has(\"name\", 
P.eq(__.addV(\"x\").values(\"name\")))",
+                "canonical": "g.V().has(\"name\", 
P.eq(__.addV(\"x\").values(\"name\")))",
+                "anonymized": "g.V().has(string0, 
P.eq(__.addV(string1).values(string0)))",
+                "dotnet": "g.V().Has(\"name\", P.Eq(__.AddV((string) 
\"x\").Values<object>(\"name\")))",
+                "go": "g.V().Has(\"name\", 
gremlingo.P.Eq(gremlingo.T__.AddV(\"x\").Values(\"name\")))",
+                "groovy": "g.V().has(\"name\", 
P.eq(__.addV(\"x\").values(\"name\")))",
+                "java": "g.V().has(\"name\", 
P.eq(__.addV(\"x\").values(\"name\")))",
+                "javascript": "g.V().has(\"name\", 
P.eq(__.addV(\"x\").values(\"name\")))",
+                "python": "g.V().has('name', 
P.eq(__.add_v('x').values('name')))"
+            }
+        ]
+    },
+    {
+        "scenario": "g_V_hasXname_withinXaddVXxX_valuesXnameXXX_rejected",
+        "traversals": [
+            {
+                "original": "g.V().has(\"name\", 
P.within(__.addV(\"x\").values(\"name\")))",
+                "language": "g.V().has(\"name\", 
P.within(__.addV(\"x\").values(\"name\")))",
+                "canonical": "g.V().has(\"name\", 
P.within(__.addV(\"x\").values(\"name\")))",
+                "anonymized": "g.V().has(string0, 
P.within(__.addV(string1).values(string0)))",
+                "dotnet": "g.V().Has(\"name\", P.Within(__.AddV((string) 
\"x\").Values<object>(\"name\")))",
+                "go": "g.V().Has(\"name\", 
gremlingo.P.Within(gremlingo.T__.AddV(\"x\").Values(\"name\")))",
+                "groovy": "g.V().has(\"name\", 
P.within(__.addV(\"x\").values(\"name\")))",
+                "java": "g.V().has(\"name\", 
P.within(__.addV(\"x\").values(\"name\")))",
+                "javascript": "g.V().has(\"name\", 
P.within(__.addV(\"x\").values(\"name\")))",
+                "python": "g.V().has('name', 
P.within(__.add_v('x').values('name')))"
+            }
+        ]
+    },
+    {
+        "scenario": "g_V_hasXage_gtXaddVXxX_valuesXageXXX_rejected",
+        "traversals": [
+            {
+                "original": "g.V().has(\"age\", 
P.gt(__.addV(\"x\").values(\"age\")))",
+                "language": "g.V().has(\"age\", 
P.gt(__.addV(\"x\").values(\"age\")))",
+                "canonical": "g.V().has(\"age\", 
P.gt(__.addV(\"x\").values(\"age\")))",
+                "anonymized": "g.V().has(string0, 
P.gt(__.addV(string1).values(string0)))",
+                "dotnet": "g.V().Has(\"age\", P.Gt(__.AddV((string) 
\"x\").Values<object>(\"age\")))",
+                "go": "g.V().Has(\"age\", 
gremlingo.P.Gt(gremlingo.T__.AddV(\"x\").Values(\"age\")))",
+                "groovy": "g.V().has(\"age\", 
P.gt(__.addV(\"x\").values(\"age\")))",
+                "java": "g.V().has(\"age\", 
P.gt(__.addV(\"x\").values(\"age\")))",
+                "javascript": "g.V().has(\"age\", 
P.gt(__.addV(\"x\").values(\"age\")))",
+                "python": "g.V().has('age', P.gt(__.add_v('x').values('age')))"
+            }
+        ]
+    },
+    {
+        "scenario": "g_V_valuesXageX_isXaddVXxX_valuesXageXX_rejected",
+        "traversals": [
+            {
+                "original": 
"g.V().values(\"age\").is(__.addV(\"x\").values(\"age\"))",
+                "language": 
"g.V().values(\"age\").is(__.addV(\"x\").values(\"age\"))",
+                "canonical": 
"g.V().values(\"age\").is(__.addV(\"x\").values(\"age\"))",
+                "anonymized": 
"g.V().values(string0).is(__.addV(string1).values(string0))",
+                "dotnet": "g.V().Values<object>(\"age\").Is(__.AddV((string) 
\"x\").Values<object>(\"age\"))",
+                "go": 
"g.V().Values(\"age\").Is(gremlingo.T__.AddV(\"x\").Values(\"age\"))",
+                "groovy": 
"g.V().values(\"age\").is(__.addV(\"x\").values(\"age\"))",
+                "java": 
"g.V().values(\"age\").is(__.addV(\"x\").values(\"age\"))",
+                "javascript": 
"g.V().values(\"age\").is(__.addV(\"x\").values(\"age\"))",
+                "python": 
"g.V().values('age').is_(__.add_v('x').values('age'))"
+            }
+        ]
+    },
+    {
+        "scenario": "g_V_valuesXageX_isXgtXaddVXxX_valuesXageXXX_rejected",
+        "traversals": [
+            {
+                "original": 
"g.V().values(\"age\").is(P.gt(__.addV(\"x\").values(\"age\")))",
+                "language": 
"g.V().values(\"age\").is(P.gt(__.addV(\"x\").values(\"age\")))",
+                "canonical": 
"g.V().values(\"age\").is(P.gt(__.addV(\"x\").values(\"age\")))",
+                "anonymized": 
"g.V().values(string0).is(P.gt(__.addV(string1).values(string0)))",
+                "dotnet": 
"g.V().Values<object>(\"age\").Is(P.Gt(__.AddV((string) 
\"x\").Values<object>(\"age\")))",
+                "go": 
"g.V().Values(\"age\").Is(gremlingo.P.Gt(gremlingo.T__.AddV(\"x\").Values(\"age\")))",
+                "groovy": 
"g.V().values(\"age\").is(P.gt(__.addV(\"x\").values(\"age\")))",
+                "java": 
"g.V().values(\"age\").is(P.gt(__.addV(\"x\").values(\"age\")))",
+                "javascript": 
"g.V().values(\"age\").is(P.gt(__.addV(\"x\").values(\"age\")))",
+                "python": 
"g.V().values('age').is_(P.gt(__.add_v('x').values('age')))"
+            }
+        ]
+    },
+    {
+        "scenario": "g_V_VXaddVXxX_idX_rejected",
+        "traversals": [
+            {
+                "original": "g.V().V(__.addV(\"x\").id())",
+                "language": "g.V().V(__.addV(\"x\").id())",
+                "canonical": "g.V().V(__.addV(\"x\").id())",
+                "anonymized": "g.V().V(__.addV(string0).id())",
+                "dotnet": "g.V().V(__.AddV((string) \"x\").Id())",
+                "go": "g.V().V(gremlingo.T__.AddV(\"x\").Id())",
+                "groovy": "g.V().V(__.addV(\"x\").id())",
+                "java": "g.V().V(__.addV(\"x\").id())",
+                "javascript": "g.V().V(__.addV(\"x\").id())",
+                "python": "g.V().V(__.add_v('x').id_())"
+            }
+        ]
+    },
+    {
+        "scenario": "g_V_EXaddVXxX_idX_rejected",
+        "traversals": [
+            {
+                "original": "g.V().E(__.addV(\"x\").id())",
+                "language": "g.V().E(__.addV(\"x\").id())",
+                "canonical": "g.V().E(__.addV(\"x\").id())",
+                "anonymized": "g.V().E(__.addV(string0).id())",
+                "dotnet": "g.V().E(__.AddV((string) \"x\").Id())",
+                "go": "g.V().E(gremlingo.T__.AddV(\"x\").Id())",
+                "groovy": "g.V().E(__.addV(\"x\").id())",
+                "java": "g.V().E(__.addV(\"x\").id())",
+                "javascript": "g.V().E(__.addV(\"x\").id())",
+                "python": "g.V().E(__.add_v('x').id_())"
+            }
+        ]
+    },
+    {
+        "scenario": "g_VXaddVXxX_idX_rejected",
+        "traversals": [
+            {
+                "original": "g.V(__.addV(\"x\").id())",
+                "language": "g.V(__.addV(\"x\").id())",
+                "canonical": "g.V(__.addV(\"x\").id())",
+                "anonymized": "g.V(__.addV(string0).id())",
+                "dotnet": "g.V(__.AddV((string) \"x\").Id())",
+                "go": "g.V(gremlingo.T__.AddV(\"x\").Id())",
+                "groovy": "g.V(__.addV(\"x\").id())",
+                "java": "g.V(__.addV(\"x\").id())",
+                "javascript": "g.V(__.addV(\"x\").id())",
+                "python": "g.V(__.add_v('x').id_())"
+            }
+        ]
+    },
+    {
+        "scenario": "g_EXaddVXxX_idX_rejected",
+        "traversals": [
+            {
+                "original": "g.E(__.addV(\"x\").id())",
+                "language": "g.E(__.addV(\"x\").id())",
+                "canonical": "g.E(__.addV(\"x\").id())",
+                "anonymized": "g.E(__.addV(string0).id())",
+                "dotnet": "g.E(__.AddV((string) \"x\").Id())",
+                "go": "g.E(gremlingo.T__.AddV(\"x\").Id())",
+                "groovy": "g.E(__.addV(\"x\").id())",
+                "java": "g.E(__.addV(\"x\").id())",
+                "javascript": "g.E(__.addV(\"x\").id())",
+                "python": "g.E(__.add_v('x').id_())"
+            }
+        ]
+    },
+    {
+        "scenario": "g_V_propertyXV_mapXdropX_projectXxX_byXnameXX_rejected",
+        "traversals": [
+            {
+                "original": 
"g.V().property(__.V().map(__.drop()).project(\"x\").by(\"name\"))",
+                "language": 
"g.V().property(__.V().map(__.drop()).project(\"x\").by(\"name\"))",
+                "canonical": 
"g.V().property(__.V().map(__.drop()).project(\"x\").by(\"name\"))",
+                "anonymized": 
"g.V().property(__.V().map(__.drop()).project(string0).by(string1))",
+                "dotnet": "g.V().Property((ITraversal) 
__.V().Map<object>(__.Drop()).Project<object>(\"x\").By(\"name\"))",
+                "go": 
"g.V().Property(gremlingo.T__.V().Map(gremlingo.T__.Drop()).Project(\"x\").By(\"name\"))",
+                "groovy": 
"g.V().property(__.V().map(__.drop()).project(\"x\").by(\"name\"))",
+                "java": 
"g.V().property(__.V().map(__.drop()).project(\"x\").by(\"name\"))",
+                "javascript": 
"g.V().property(__.V().map(__.drop()).project(\"x\").by(\"name\"))",
+                "python": 
"g.V().property(__.V().map(__.drop()).project('x').by('name'))"
+            }
+        ]
+    },
+    {
+        "scenario": "g_V_hasXname_VXvid1X_valuesXnameXX_passes_verification",
+        "traversals": [
+            {
+                "original": "g.V().has(\"name\", __.V(vid1).values(\"name\"))",
+                "language": "g.V().has(\"name\", __.V(vid1).values(\"name\"))",
+                "canonical": "g.V().has(\"name\", 
__.V(vid1).values(\"name\"))",
+                "anonymized": "g.V().has(string0, __.V(vid1).values(string0))",
+                "dotnet": "g.V().Has(\"name\", 
__.V(vid1).Values<object>(\"name\"))",
+                "go": "g.V().Has(\"name\", 
gremlingo.T__.V(vid1).Values(\"name\"))",
+                "groovy": "g.V().has(\"name\", __.V(vid1).values(\"name\"))",
+                "java": "g.V().has(\"name\", __.V(vid1).values(\"name\"))",
+                "javascript": "g.V().has(\"name\", 
__.V(vid1).values(\"name\"))",
+                "python": "g.V().has('name', __.V(vid1).values('name'))"
+            }
+        ]
+    },
+    {
+        "scenario": "g_V_hasXage_gtXVXvid1X_valuesXageXXX_passes_verification",
+        "traversals": [
+            {
+                "original": "g.V().has(\"age\", 
P.gt(__.V(vid1).values(\"age\")))",
+                "language": "g.V().has(\"age\", 
P.gt(__.V(vid1).values(\"age\")))",
+                "canonical": "g.V().has(\"age\", 
P.gt(__.V(vid1).values(\"age\")))",
+                "anonymized": "g.V().has(string0, 
P.gt(__.V(vid1).values(string0)))",
+                "dotnet": "g.V().Has(\"age\", 
P.Gt(__.V(vid1).Values<object>(\"age\")))",
+                "go": "g.V().Has(\"age\", 
gremlingo.P.Gt(gremlingo.T__.V(vid1).Values(\"age\")))",
+                "groovy": "g.V().has(\"age\", 
P.gt(__.V(vid1).values(\"age\")))",
+                "java": "g.V().has(\"age\", P.gt(__.V(vid1).values(\"age\")))",
+                "javascript": "g.V().has(\"age\", 
P.gt(__.V(vid1).values(\"age\")))",
+                "python": "g.V().has('age', P.gt(__.V(vid1).values('age')))"
+            }
+        ]
+    },
+    {
+        "scenario": "g_V_VXoutXknowsX_idX_valuesXnameX_passes_verification",
+        "traversals": [
+            {
+                "original": 
"g.V(vid1).V(__.out(\"knows\").id()).values(\"name\")",
+                "language": 
"g.V(vid1).V(__.out(\"knows\").id()).values(\"name\")",
+                "canonical": 
"g.V(vid1).V(__.out(\"knows\").id()).values(\"name\")",
+                "anonymized": 
"g.V(vid1).V(__.out(string0).id()).values(string1)",
+                "dotnet": 
"g.V(vid1).V(__.Out(\"knows\").Id()).Values<object>(\"name\")",
+                "go": 
"g.V(vid1).V(gremlingo.T__.Out(\"knows\").Id()).Values(\"name\")",
+                "groovy": 
"g.V(vid1).V(__.out(\"knows\").id()).values(\"name\")",
+                "java": "g.V(vid1).V(__.out(\"knows\").id()).values(\"name\")",
+                "javascript": 
"g.V(vid1).V(__.out(\"knows\").id()).values(\"name\")",
+                "python": "g.V(vid1).V(__.out('knows').id_()).values('name')"
+            }
+        ]
+    },
     {
         "scenario": "g_V_coinX1_0X",
         "traversals": [
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/ChildTraversalVerification.feature
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/ChildTraversalVerification.feature
new file mode 100644
index 0000000000..1bd57ef418
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/ChildTraversalVerification.feature
@@ -0,0 +1,202 @@
+# 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.
+
+@StepClassFilter @StepHas
+Feature: Child Traversal Verification - mutating steps blocked in 
filter/lookup contexts
+
+  # ===== FILTER CONTEXT: has() with mutating child traversals =====
+  # All error scenarios use addV()/drop() which ComputerVerificationStrategy 
also rejects on OLAP
+  # with a different message, so we exclude them from GraphComputer runs.
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXname_addVXxX_valuesXnameXX_rejected
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().has("name", __.addV("x").values("name"))
+      """
+    When iterated to list
+    Then the traversal will raise an error with message containing text of 
"mutating step"
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXname_V_drop_constantXxXX_rejected
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().has("name", __.V().drop().constant("x"))
+      """
+    When iterated to list
+    Then the traversal will raise an error with message containing text of 
"mutating step"
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXname_V_mapXaddVXxXX_valuesXnameXX_rejected
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().has("name", __.V().map(__.addV("x")).values("name"))
+      """
+    When iterated to list
+    Then the traversal will raise an error with message containing text of 
"mutating step"
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXname_eqXaddVXxX_valuesXnameXXX_rejected
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().has("name", P.eq(__.addV("x").values("name")))
+      """
+    When iterated to list
+    Then the traversal will raise an error with message containing text of 
"mutating step"
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXname_withinXaddVXxX_valuesXnameXXX_rejected
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().has("name", P.within(__.addV("x").values("name")))
+      """
+    When iterated to list
+    Then the traversal will raise an error with message containing text of 
"mutating step"
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXage_gtXaddVXxX_valuesXageXXX_rejected
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().has("age", P.gt(__.addV("x").values("age")))
+      """
+    When iterated to list
+    Then the traversal will raise an error with message containing text of 
"mutating step"
+
+  # ===== FILTER CONTEXT: is() with mutating child traversals =====
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_valuesXageX_isXaddVXxX_valuesXageXX_rejected
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().values("age").is(__.addV("x").values("age"))
+      """
+    When iterated to list
+    Then the traversal will raise an error with message containing text of 
"mutating step"
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_valuesXageX_isXgtXaddVXxX_valuesXageXXX_rejected
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().values("age").is(P.gt(__.addV("x").values("age")))
+      """
+    When iterated to list
+    Then the traversal will raise an error with message containing text of 
"mutating step"
+
+  # ===== LOOKUP CONTEXT: V()/E() with mutating child traversals =====
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_VXaddVXxX_idX_rejected
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().V(__.addV("x").id())
+      """
+    When iterated to list
+    Then the traversal will raise an error with message containing text of 
"mutating step"
+
+  @GraphComputerVerificationMidENotSupported
+  Scenario: g_V_EXaddVXxX_idX_rejected
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().E(__.addV("x").id())
+      """
+    When iterated to list
+    Then the traversal will raise an error with message containing text of 
"mutating step"
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_VXaddVXxX_idX_rejected
+    Given the modern graph
+    And the traversal of
+      """
+      g.V(__.addV("x").id())
+      """
+    When iterated to list
+    Then the traversal will raise an error with message containing text of 
"mutating step"
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_EXaddVXxX_idX_rejected
+    Given the modern graph
+    And the traversal of
+      """
+      g.E(__.addV("x").id())
+      """
+    When iterated to list
+    Then the traversal will raise an error with message containing text of 
"mutating step"
+
+  # ===== MUTATION CONTEXT: property(traversal) blocks DropStep but allows 
other mutations =====
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_propertyXV_mapXdropX_projectXxX_byXnameXX_rejected
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().property(__.V().map(__.drop()).project("x").by("name"))
+      """
+    When iterated to list
+    Then the traversal will raise an error with message containing text of 
"DropStep"
+
+  # ===== VALID TRAVERSALS: should NOT be rejected =====
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXname_VXvid1X_valuesXnameXX_passes_verification
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().has("name", __.V(vid1).values("name"))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | v[marko] |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXage_gtXVXvid1X_valuesXageXXX_passes_verification
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().has("age", P.gt(__.V(vid1).values("age")))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | v[josh] |
+      | v[peter] |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_VXoutXknowsX_idX_valuesXnameX_passes_verification
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V(vid1).V(__.out("knows").id()).values("name")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | vadas |
+      | josh |

Reply via email to