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

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

commit 83fed48ba027a427866248261d5601ba85363a12
Author: Yang Xia <[email protected]>
AuthorDate: Fri Jun 12 00:20:19 2026 -0700

    Add feature conformance tests and documentation for traversal-accepting 
steps
    
    Add cross-language Gherkin scenarios covering has/is/where/property/V/E with
    child-traversal arguments and the child-traversal mutation verification, 
plus
    CHANGELOG, upgrade, and reference documentation describing the feature, its
    first-result/empty-set semantics, and the provider-side HasContainer folding
    guard.
---
 CHANGELOG.asciidoc                                 |   9 +
 docs/src/reference/the-traversal.asciidoc          | 103 +++++++
 docs/src/upgrade/release-4.x.x.asciidoc            |  41 +++
 .../filter/ChildTraversalVerification.feature      | 212 ++++++++++++++
 .../test/features/filter/HasTraversal.feature      | 326 +++++++++++++++++++++
 .../test/features/filter/IsTraversal.feature       | 273 +++++++++++++++++
 .../test/features/filter/WhereTraversal.feature    | 122 ++++++++
 .../gremlin/test/features/map/VETraversal.feature  | 149 ++++++++++
 .../features/sideEffect/PropertyTraversal.feature  | 253 ++++++++++++++++
 9 files changed, 1488 insertions(+)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 4971f79217..f035157d1d 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -34,6 +34,15 @@ 
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 * Added explicit transaction support to all non-Java GLVs (gremlin-python, 
gremlin-go, gremlin-javascript, gremlin-dotnet).
 * Changed default transaction close behavior from commit to rollback across 
all GLVs to align with embedded graph defaults.
 * Refactored Go driver connection to block until response headers arrive, 
enabling synchronous error returns and proper transaction ordering.
+* Added child traversal support to `has()`, `hasLabel()`, `V()`, `E()`, 
`property()`, `is()`, `where(P)`, `P.eq/neq/gt/lt/gte/lte/within/without()`, 
and `TextP` predicates. Child traversals are resolved per-traverser at runtime, 
enabling dynamic filtering and lookup patterns.
+* Added multi-traversal `P.within(trav1, trav2, ...)` and `P.without(trav1, 
trav2, ...)` which combine results from multiple child traversals for 
collection membership testing.
+* Added `V(traversal)` and `E(traversal)` as start steps with synthetic 
traverser seeding, consistent with `mergeV(traversal)` behavior.
+* Added `ChildTraversalVerificationStrategy` that blocks mutating steps 
(`addV`, `addE`, `drop`, etc.) inside child traversals. All child traversals 
must be read-only.
+* Added `hasLabel(Traversal)` overload with grammar support for dynamic label 
filtering.
+* Added traversal-bearing predicate support to `where(P)` - resolves child 
traversal and tests against current value when `P.hasTraversal()` is true.
+* Added rejection of traversal-bearing predicates in `choose(P)` - use 
`choose(__.is(P.op(traversal)), ...)` form instead.
+* Added runtime Map validation for `property(traversal)` - rejects results 
that are not a `Map` of property key/value pairs.
+* Added mixed traversal/literal detection in `P.within()` and `P.without()` - 
throws `IllegalArgumentException` with guidance to wrap literals in 
`__.constant()`.
 * Removed `uuid` dependency from `gremlin-javascript` in favor of the built-in 
`globalThis.crypto.randomUUID()`.
 * Added streaming HTTP response support to `gremlin-driver` for incremental 
result deserialization over GraphBinary.
 * Connected HTTP streaming response deserialization to the traversal API in 
`gremlin-javascript`, enabling `next()` to return the first result without 
waiting for the full response.
diff --git a/docs/src/reference/the-traversal.asciidoc 
b/docs/src/reference/the-traversal.asciidoc
index bc05a52b44..790873c581 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -1253,6 +1253,17 @@ IMPORTANT: It is important to think of `choose()` as a 
branching step and not a
 intuitively lead to thinking the latter, where no match would mean to remove 
the traverser from the stream. As shown in
 the examples, this is not what happens.
 
+NOTE: The `choose(P)` form does not support traversal-bearing predicates 
directly. To use a dynamic comparison as the
+branch condition, wrap it in an `is()` step: `choose(__.is(P.op(traversal)), 
trueChoice, falseChoice)`.
+
+[gremlin-groovy,modern]
+----
+g.V().values('age').choose(__.is(P.gt(__.V(1).values('age'))), 
__.constant('older'), __.constant('not older')) <1>
+----
+
+<1> For each age value, compare dynamically against marko's age using a 
traversal inside the predicate. If greater,
+return "older"; otherwise return "not older".
+
 The `choose()`-step can be used within a `map()` step to apply the branching 
logic to each element in a collection.
 
 [gremlin-groovy,modern]
@@ -2074,6 +2085,10 @@ It is possible to filter vertices, edges, and vertex 
properties based on their p
   * `has(key,value)`: Remove the traverser if its element does not have the 
provided key/value property.
   * `has(label, key, value)`: Remove the traverser if its element does not 
have the specified label and provided key/value property.
   * `has(key,predicate)`: Remove the traverser if its element does not have a 
key value that satisfies the bi-predicate. For more information on predicates, 
please read <<a-note-on-predicates,A Note on Predicates>>.
+  * `has(key, traversal)`: Remove the traverser if its element's key value 
does not equal the first result of the provided traversal.
+  * `has(T, traversal)`: Remove the traverser if its element's `T`-based value 
(e.g. `T.id`, `T.label`) does not equal the first result of the provided 
traversal.
+  * `has(label, key, traversal)`: Remove the traverser if its element does not 
have the specified label and its key value does not equal the first result of 
the provided traversal.
+  * `hasLabel(traversal)`: Remove the traverser if its element's label does 
not equal the first result of the provided traversal.
   * `hasLabel(labels...)`: Remove the traverser if its element does not have 
any of the labels.
   * `hasId(ids...)`: Remove the traverser if its element does not have any of 
the ids.
   * `hasKey(keys...)`: Remove the `Property` traverser if it does not match 
one of the provided keys.
@@ -2112,6 +2127,24 @@ the key,value pairs for those vertices.
 <9> Property key is always stored as `String` and therefore an equality check 
with `null` will produce no result.
 <10> An example of using `has()` with regular expression predicate.
 
+The traversal-accepting forms of `has()` allow for dynamic property 
comparisons. Rather than providing a literal value,
+a child traversal is supplied and its first result is used as the comparison 
value. This follows the same first-result
+semantics as `by(traversal)`. The child traversal must be read-only - mutating 
steps are rejected.
+
+[gremlin-groovy,modern]
+----
+g.V().has('age', P.gt(__.V(1).values('age'))) <1>
+g.V().has('name', __.V(1).values('name')) <2>
+g.V().hasLabel(__.V(1).label()) <3>
+----
+
+<1> Find all vertices whose age is greater than marko's age using a traversal 
inside a predicate.
+<2> Find all vertices whose name equals marko's name using a traversal as the 
value argument.
+<3> Find all vertices whose label equals the label of vertex 1.
+
+NOTE: When a `Traversal` is provided directly as the value argument (not 
inside a `P`), it is internally wrapped in
+`P.eq(traversal)`. See <<a-note-on-predicates,A Note on Predicates>> for more 
details on traversal-bearing predicates.
+
 *Additional References*
 
 
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#has(java.lang.String)++[`has(String)`],
@@ -2467,6 +2500,18 @@ g.V().where(__.in('created').values('age').
 <2> Find projects having two or more contributors.
 <3> Find projects whose contributors average age is between 30 and 35.
 
+The `is()` step also supports predicates that contain traversal arguments for 
dynamic threshold comparison.
+
+[gremlin-groovy,modern]
+----
+g.V().values('age').is(P.gt(__.V(1).values('age'))) <1>
+----
+
+<1> Find all age values that are greater than marko's age using a traversal 
inside the predicate.
+
+NOTE: When `is(traversal)` is used directly (without an explicit predicate), 
it is internally wrapped in
+`P.eq(traversal)`.
+
 *Additional References*
 
 
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#is(java.lang.Object)++[`is(Object)`],
@@ -3989,6 +4034,21 @@ g.addV().property(set, null)
 <7> The label value can be specified as a property only at the time a vertex 
is added and if one is not specified in the addV()
 <8> If you pass a `null` value for the Map this will be treated as a no-op and 
the input will be returned
 
+The `property()` step also accepts a traversal that produces a `Map` of 
key-value pairs to set as properties. The
+child traversal must be read-only - mutating steps are rejected. If the 
traversal does not produce a `Map`, the result
+is rejected. Each entry in the resulting Map becomes a separate property on 
the element, allowing multiple properties
+to be set in a single step.
+
+[gremlin-groovy,modern]
+----
+g.V(4).property(__.V(1).project('friendCount','createdSoftware').by(__.out('knows').count()).by(__.out('created').values('name')))
 <1>
+g.V(4).valueMap('friendCount','createdSoftware')
+----
+
+<1> Set two properties on vertex 4 (josh) from a Map produced by a child 
traversal. The `project()` step builds a Map
+with keys "friendCount" and "createdSoftware", whose values are computed from 
vertex 1's (marko's) relationships -
+number of friends and name of software created. Both properties are applied to 
vertex 4 in one operation.
+
 *Additional References*
 
 
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#property(java.lang.Object,java.lang.Object,java.lang.Object...)++[`property(Object,
 Object, Object...)`],
@@ -5187,6 +5247,19 @@ g.V().has('name', within('marko', 'vadas', 
'josh')).as('person').
 <1> Normally the `V()`-step will iterate over all vertices. However, graph 
strategies can fold ``HasContainer``'s into a `GraphStep` to allow index 
lookups.
 <2> Whether the graph system provider supports mid-traversal `V()` index 
lookups or not can easily be determined by inspecting the `toString()` output 
of the iterated traversal. If `has` conditions were folded into the `V()`-step, 
an index - if one exists - will be used.
 
+The `V()` step also accepts a traversal argument. The child traversal is 
evaluated and its results are used as the
+vertex identifiers. This works both as a start step and mid-traversal. When 
used as a start step, a synthetic traverser
+is provided to the child traversal.
+
+[gremlin-groovy,modern]
+----
+g.V(__.V(1).id()).values('name') <1>
+g.inject(1).V(__.identity()).values('name') <2>
+----
+
+<1> Use a child traversal to dynamically determine the vertex ID. Here the 
traversal resolves vertex 1's ID.
+<2> Mid-traversal usage where the injected value is used via `identity()` as 
the ID argument to `V()`.
+
 *Additional References*
 
 
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#V(java.lang.Object...)++[`V(Object...)`]
@@ -5399,6 +5472,18 @@ g.V().as('a').both().both().as('b').
 <8> Marko is younger than josh, but josh knows someone equal in age to marko 
(which is marko).
 <9> The "age" property is not <<by-step,productive>> for all vertices and 
therefore those values are filtered.
 
+The `where()` step also supports predicates that contain traversal arguments. 
When a predicate contains a child
+traversal, the traversal is resolved per-traverser and the result is tested 
directly against the current value -
+bypassing scope-label resolution.
+
+[gremlin-groovy,modern]
+----
+g.V().values('age').where(P.gt(__.V(1).values('age'))) <1>
+----
+
+<1> Find all age values that are greater than marko's age. The child traversal 
inside `P.gt()` is resolved and the
+current traverser's value is compared against the result.
+
 WARNING: The anonymous traversal of `where()` processes the current object 
"locally". In OLAP, where the atomic unit
 of computing is the vertex and its local "star graph," it is important that 
the anonymous traversal does not leave
 the confines of the vertex's star graph. In other words, it can not traverse 
to an adjacent vertex's properties or
@@ -5524,6 +5609,24 @@ NOTE: The TinkerPop reference implementation uses the 
Java `Pattern` and `Matche
 engine. Other implementations may decide to use a different regular expression 
engine. It's a good idea to check
 the documentation for the implementation you are using to verify the allowed 
regular expression syntax.
 
+In addition to literal values, most predicates also accept a `Traversal` 
argument. When a traversal is provided, it is
+evaluated and only its first result is used for comparison - consistent with 
`by(traversal)` first-result semantics.
+The child traversal must be read-only; mutating steps are rejected.
+
+* For `within()` and `without()`, the first result should be a `Collection`. 
Use `fold()` in the child traversal to
+  produce one.
+* The multi-traversal form `within(trav1, trav2, ...)` takes the first result 
from each traversal and combines them
+  into a collection for membership testing.
+
+[gremlin-groovy,modern]
+----
+g.V().has('age', P.gt(__.V(1).values('age'))) <1>
+g.V().has('age', P.within(__.V(1).out('knows').values('age').fold())) <2>
+----
+
+<1> Find vertices whose age is greater than marko's age using a traversal 
inside the predicate.
+<2> Find vertices whose age is in the set of ages of marko's friends, using 
`fold()` to produce a collection.
+
 [gremlin-groovy]
 ----
 eq(2)
diff --git a/docs/src/upgrade/release-4.x.x.asciidoc 
b/docs/src/upgrade/release-4.x.x.asciidoc
index ac8debb0a9..ed92ed73a1 100644
--- a/docs/src/upgrade/release-4.x.x.asciidoc
+++ b/docs/src/upgrade/release-4.x.x.asciidoc
@@ -69,6 +69,32 @@ GLVs (including the Java driver). This aligns with the 
embedded graph transactio
 partial work is discarded if the user forgets to call `commit()`. In Java 
(both remote and embedded mode), the behavior
 can still be overridden via `tx.onClose(Transaction.CLOSE_BEHAVIOR.COMMIT)`. 
The non-Java GLVs do not support
 configuring close behavior and always rollback.
+==== Traversal-Accepting Steps
+
+Steps and predicates that previously only accepted literal values now accept 
child traversals resolved per-traverser
+at runtime. This enables dynamic filtering, lookup, and mutation patterns 
without `where()`/`select()` workarounds.
+
+Affected steps: `has()`, `hasLabel()`, `V()`, `E()`, `property()`, `is()`, 
`where(P)`, `P.eq/neq/gt/lt/gte/lte()`,
+`P.within/without()`, and all `TextP` predicates.
+
+[source,groovy]
+----
+// Dynamic property comparison
+g.V().has("age", P.gt(__.V(1).values("age")))
+
+// Dynamic vertex lookup: resolve the ids of marko's friends, then look them 
up by id
+g.V(1).V(__.out("knows").id()).values("name")
+
+// Multi-source filtering with within()
+g.V().has("name", P.within(__.V(1).out("knows").values("name").fold(), 
__.constant("peter")))
+----
+
+Child traversals take only the first result (consistent with `by(traversal)` 
semantics). For `within()`/`without()`,
+use `fold()` to collect multiple values into a list.
+
+All child traversals must be read-only. Mutating steps (`addV`, `addE`, 
`drop`, `property`) inside child traversals
+are rejected at construction time with `IllegalArgumentException`. A 
`ChildTraversalVerificationStrategy` provides
+additional safety at strategy time.
 
 ==== Removed `uuid` Dependency in gremlin-javascript
 
@@ -565,6 +591,21 @@ benefiting all driver users transparently.
 See <<provider-defined-types>> for full details on annotation usage, field 
filtering, nested types, and ServiceLoader
 registration.
 
+===== Traversal-Accepting Steps - HasContainer Guard
+
+Steps and predicates now accept child traversals resolved at runtime. 
`HasContainer` instances that hold a child
+traversal cannot be folded into index lookups because their value is dynamic 
(resolved per-traverser). Providers that
+fold `HasContainer` predicates into their graph step must guard against this:
+
+[source,java]
+----
+if (hasContainer.hasTraversal()) continue;  // skip - dynamic value, cannot 
fold into index
+----
+
+`GraphStep.processHasContainerIds()` already includes this guard. Providers 
that implement their own `HasContainer`
+folding strategy should add the same check. Without it, the container's value 
is read before it has been resolved
+against a traverser, so an unresolved value would be folded into the index 
lookup and produce incorrect results.
+
 ==== Graph Driver Providers
 
 == TinkerPop 4.0.0-beta.2
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..eacf7a972a
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/ChildTraversalVerification.feature
@@ -0,0 +1,212 @@
+# 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 ALL mutating steps =====
+
+  @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 
"mutating step"
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_propertyXaddVXtempX_projectXkX_byXnameXX_rejected
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().property(__.addV("temp").project("k").by("name"))
+      """
+    When iterated to list
+    Then the traversal will raise an error with message containing text of 
"mutating step"
+
+  # ===== 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 |
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/HasTraversal.feature
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/HasTraversal.feature
new file mode 100644
index 0000000000..74e194ba65
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/HasTraversal.feature
@@ -0,0 +1,326 @@
+# 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: Step - has() with traversal arguments
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXname_VXvid1X_valuesXnameXX
+    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
+  # has(key, traversal) with multi-result child traversal - takes first result 
(order-dependent)
+  @InsertionOrderingRequired
+  Scenario: g_V_hasXname_VXvid1X_outXknowsX_valuesXnameXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().has("name", __.V(vid1).out("knows").values("name"))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | v[vadas] |
+
+  # has(key, traversal) with multi-result child traversal (age) - takes first 
result (order-dependent)
+  @GraphComputerVerificationMidVNotSupported
+  @InsertionOrderingRequired
+  Scenario: g_V_hasXage_VXvid1X_outXknowsX_valuesXageXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().has("age", __.V(vid1).out("knows").values("age"))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | v[vadas] |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXname_VXvid1X_valuesXnonexistentXX
+    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("nonexistent"))
+      """
+    When iterated to list
+    Then the result should be empty
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXname_notXidentityXX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().has("name", __.not(__.identity()))
+      """
+    When iterated to list
+    Then the result should be empty
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXage_gtXVXvid1X_valuesXageXXX
+    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_hasXage_lteXVXvid2X_valuesXageXXX
+    Given the modern graph
+    And using the parameter vid2 defined as "v[vadas].id"
+    And the traversal of
+      """
+      g.V().has("age", P.lte(__.V(vid2).values("age")))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | v[vadas] |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXage_neqXVXvid3X_valuesXageXXX
+    Given the modern graph
+    And using the parameter vid3 defined as "v[josh].id"
+    And the traversal of
+      """
+      g.V().has("age", P.neq(__.V(vid3).values("age")))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | v[marko] |
+      | v[vadas] |
+      | v[peter] |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXname_eqXVXvid1X_valuesXnameXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().has("name", P.eq(__.V(vid1).values("name")))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | v[marko] |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXage_ltXVXvid1X_valuesXageXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().has("age", P.lt(__.V(vid1).values("age")))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | v[vadas] |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXage_gteXVXvid3X_valuesXageXXX
+    Given the modern graph
+    And using the parameter vid3 defined as "v[josh].id"
+    And the traversal of
+      """
+      g.V().has("age", P.gte(__.V(vid3).values("age")))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | v[josh] |
+      | v[peter] |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXage_eqXVXvid1X_valuesXnonexistentXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().has("age", P.eq(__.V(vid1).values("nonexistent")))
+      """
+    When iterated to list
+    Then the result should be empty
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXlabel_VXvid1X_labelXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().has(T.label, __.V(vid1).label())
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | v[marko] |
+      | v[vadas] |
+      | v[josh] |
+      | v[peter] |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXperson_name_VXvid1X_valuesXnameXX_age
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().has("person", "name", __.V(vid1).values("name")).values("age")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[29].i |
+
+  # Multi-traversal within() where one traversal produces multiple results - 
use fold() to collect
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: 
g_V_hasXname_withinXVXvid1X_outXknowsX_valuesXnameX_constantXpeterXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().has("name", 
P.within(__.V(vid1).out("knows").values("name").fold(), __.constant("peter")))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | v[vadas] |
+      | v[josh] |
+      | v[peter] |
+
+  # Multi-traversal within() where one traversal produces no results - still 
matches on the other
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXname_withinXVXvid1X_valuesXnonexistentX_constantXmarkoXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().has("name", P.within(__.V(vid1).values("nonexistent"), 
__.constant("marko")))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | v[marko] |
+
+  # Multi-traversal within() where all traversals produce no results - filters 
everything
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: 
g_V_hasXname_withinXVXvid1X_valuesXnonexistentX_VXvid1X_valuesXnonexistentXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().has("name", P.within(__.V(vid1).values("nonexistent"), 
__.V(vid1).values("nonexistent")))
+      """
+    When iterated to list
+    Then the result should be empty
+
+  # Multi-traversal without() with three traversals
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: 
g_V_hasXname_withoutXVXvid1X_valuesXnameX_VXvid2X_valuesXnameX_VXvid3X_valuesXnameXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And using the parameter vid2 defined as "v[vadas].id"
+    And using the parameter vid3 defined as "v[peter].id"
+    And the traversal of
+      """
+      g.V().has("name", P.without(__.V(vid1).values("name"), 
__.V(vid2).values("name"), __.V(vid3).values("name")))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | v[josh] |
+      | v[lop] |
+      | v[ripple] |
+
+  # Multi-traversal within() - union of relationship traversals from different 
sources
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: 
g_V_hasXname_withinXVXvid1X_outXknowsX_valuesXnameX_VXvid3X_outXcreatedX_valuesXnameXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And using the parameter vid3 defined as "v[josh].id"
+    And the traversal of
+      """
+      g.V().has("name", 
P.within(__.V(vid1).out("knows").values("name").fold(), 
__.V(vid3).out("created").values("name").fold()))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | v[vadas] |
+      | v[josh] |
+      | v[ripple] |
+      | v[lop] |
+
+  # Multi-traversal without() - exclusion from multiple relationship sources
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: 
g_V_hasLabelXsoftwareX_hasXname_withoutXVXvid1X_outXcreatedX_valuesXnameX_VXvid3X_outXcreatedX_valuesXnameXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And using the parameter vid3 defined as "v[josh].id"
+    And the traversal of
+      """
+      g.V().hasLabel("software").has("name", 
P.without(__.V(vid1).out("created").values("name").fold(), 
__.V(vid3).out("created").values("name").fold()))
+      """
+    When iterated to list
+    Then the result should be empty
+
+  # Multi-traversal within() with is() - cross-label dynamic filtering, use 
fold() for multi-result
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: 
g_V_hasLabelXpersonX_valuesXageX_isXwithinXVXvid1X_valuesXageX_V_hasXname_lopX_inXcreatedX_valuesXageXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      
g.V().hasLabel("person").values("age").is(P.within(__.V(vid1).values("age").fold(),
 __.V().has("name","lop").in("created").values("age").fold()))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[29].i |
+      | d[32].i |
+      | d[35].i |
+
+  # Multi-traversal within() - dynamic edge filtering via inV property check
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: 
g_VXvid1X_outEXknowsX_filterXinV_hasXname_withinXV_hasXname_lopX_inXcreatedX_valuesXnameX_V_hasXname_rippleX_inXcreatedX_valuesXnameXXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V(vid1).outE("knows").filter(__.inV().has("name", 
P.within(__.V().has("name","lop").in("created").values("name").fold(), 
__.V().has("name","ripple").in("created").values("name").fold())))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | e[marko-knows->josh] |
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/IsTraversal.feature
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/IsTraversal.feature
new file mode 100644
index 0000000000..6872769c9b
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/IsTraversal.feature
@@ -0,0 +1,273 @@
+# 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 @StepIs
+Feature: Step - is() with traversal-bearing predicates
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_valuesXageX_isXVXvid1X_valuesXageXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().values("age").is(__.V(vid1).values("age"))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[29].i |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_valuesXnameX_isXVXvid1X_valuesXnameXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().values("name").is(__.V(vid1).values("name"))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | marko |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_valuesXageX_isXgtXVXvid1X_valuesXageXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().values("age").is(P.gt(__.V(vid1).values("age")))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[32].i |
+      | d[35].i |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_valuesXageX_isXltXVXvid3X_valuesXageXXX
+    Given the modern graph
+    And using the parameter vid3 defined as "v[josh].id"
+    And the traversal of
+      """
+      g.V().values("age").is(P.lt(__.V(vid3).values("age")))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[29].i |
+      | d[27].i |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_valuesXageX_isXneqXVXvid4X_valuesXageXXX
+    Given the modern graph
+    And using the parameter vid4 defined as "v[peter].id"
+    And the traversal of
+      """
+      g.V().values("age").is(P.neq(__.V(vid4).values("age")))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[29].i |
+      | d[27].i |
+      | d[32].i |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_valuesXageX_isXwithinXVXvid1X_outXknowsX_valuesXageXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      
g.V().values("age").is(P.within(__.V(vid1).out("knows").values("age").fold()))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[27].i |
+      | d[32].i |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_valuesXageX_isXVXvid1X_valuesXnonexistentXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().values("age").is(__.V(vid1).values("nonexistent"))
+      """
+    When iterated to list
+    Then the result should be empty
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: 
g_V_hasLabelXpersonX_valuesXageX_chooseXgtXVXvid1X_valuesXageXX_olderX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      
g.V().hasLabel("person").values("age").choose(__.is(P.gt(__.V(vid1).values("age"))),
 __.constant("older than marko"), __.constant("not older"))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | not older |
+      | not older |
+      | older than marko |
+      | older than marko |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasLabelXpersonX_valuesXageX_chooseXgteXmeanAgeX_aboveX
+    Given the modern graph
+    And the traversal of
+      """
+      
g.V().hasLabel("person").values("age").choose(__.is(P.gte(__.V().hasLabel("person").values("age").mean())),
 __.constant("above average"), __.constant("below average"))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | below average |
+      | below average |
+      | above average |
+      | above average |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_VXvid1X_outXknowsX_valuesXageX_fold_allXgteXVXvid2X_valuesXageXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And using the parameter vid2 defined as "v[vadas].id"
+    And the traversal of
+      """
+      
g.V(vid1).out("knows").values("age").fold().all(P.gte(__.V(vid2).values("age")))
+      """
+    When iterated to list
+    Then the result should have a count of 1
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_VXvid1X_outXknowsX_valuesXageX_fold_allXgtXVXvid2X_valuesXageXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And using the parameter vid2 defined as "v[vadas].id"
+    And the traversal of
+      """
+      
g.V(vid1).out("knows").values("age").fold().all(P.gt(__.V(vid2).values("age")))
+      """
+    When iterated to list
+    Then the result should be empty
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_VXvid1X_outXknowsX_valuesXageX_fold_anyXeqXVXvid3X_valuesXageXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And using the parameter vid3 defined as "v[josh].id"
+    And the traversal of
+      """
+      
g.V(vid1).out("knows").values("age").fold().any(P.eq(__.V(vid3).values("age")))
+      """
+    When iterated to list
+    Then the result should have a count of 1
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_VXvid1X_outXknowsX_valuesXageX_fold_noneXeqXVXvid4X_valuesXageXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And using the parameter vid4 defined as "v[peter].id"
+    And the traversal of
+      """
+      
g.V(vid1).out("knows").values("age").fold().none(P.eq(__.V(vid4).values("age")))
+      """
+    When iterated to list
+    Then the result should have a count of 1
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_VXvid1X_outXknowsX_valuesXageX_fold_noneXeqXVXvid3X_valuesXageXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And using the parameter vid3 defined as "v[josh].id"
+    And the traversal of
+      """
+      
g.V(vid1).out("knows").values("age").fold().none(P.eq(__.V(vid3).values("age")))
+      """
+    When iterated to list
+    Then the result should be empty
+
+  # Empty-result handling: predicate should treat "no results" as "no match", 
not null.
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_injectXnullX_isXeqXV9999_valuesXnameXXX
+    Given the modern graph
+    And the traversal of
+      """
+      g.inject(null).is(P.eq(__.V(9999).values("name")))
+      """
+    When iterated to list
+    Then the result should be empty
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_injectXmarkoX_isXV9999_valuesXnameXX
+    Given the modern graph
+    And the traversal of
+      """
+      g.inject("marko").is(__.V(9999).values("name"))
+      """
+    When iterated to list
+    Then the result should be empty
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_injectXmarkoX_chooseXeqXV9999_valuesXnameXX_matched_unmatchedX
+    Given the modern graph
+    And the traversal of
+      """
+      g.inject("marko").choose(__.is(P.eq(__.V(9999).values("name"))), 
__.constant("matched"), __.constant("unmatched"))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | unmatched |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_injectXlistX_noneXeqXV9999_valuesXnameXXX
+    Given the modern graph
+    And the traversal of
+      """
+      g.inject(["marko","josh"]).none(P.eq(__.V(9999).values("name")))
+      """
+    When iterated to list
+    Then the result should have a count of 1
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_hasXname_eqXV9999_valuesXnameXXX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().has("name", P.eq(__.V(9999).values("name")))
+      """
+    When iterated to list
+    Then the result should be empty
+
+  # Multi-traversal without() in is() context
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: 
g_V_valuesXageX_isXwithoutXVXvid1X_valuesXageX_VXvid2X_valuesXageXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And using the parameter vid2 defined as "v[josh].id"
+    And the traversal of
+      """
+      g.V().values("age").is(P.without(__.V(vid1).values("age"), 
__.V(vid2).values("age")))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[27].i |
+      | d[35].i |
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/WhereTraversal.feature
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/WhereTraversal.feature
new file mode 100644
index 0000000000..d9529d6be8
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/WhereTraversal.feature
@@ -0,0 +1,122 @@
+# 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 @StepWhere
+Feature: Step - where(P) with traversal-bearing predicates
+
+  # where(P.gt(traversal)) - compare current traverser value against resolved 
traversal result
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_valuesXageX_whereXgtXVXvid1X_valuesXageXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().values("age").where(P.gt(__.V(vid1).values("age")))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[32].i |
+      | d[35].i |
+
+  # where(P.lt(traversal)) - filter ages less than josh's age
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_valuesXageX_whereXltXVXvid3X_valuesXageXXX
+    Given the modern graph
+    And using the parameter vid3 defined as "v[josh].id"
+    And the traversal of
+      """
+      g.V().values("age").where(P.lt(__.V(vid3).values("age")))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[29].i |
+      | d[27].i |
+
+  # where(P.eq(traversal)) - exact match against resolved value
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_valuesXageX_whereXeqXVXvid1X_valuesXageXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().values("age").where(P.eq(__.V(vid1).values("age")))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[29].i |
+
+  # where(P.within(traversal)) - collection membership against resolved 
traversal results
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_valuesXageX_whereXwithinXVXvid1X_outXknowsX_valuesXageXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      
g.V().values("age").where(P.within(__.V(vid1).out("knows").values("age").fold()))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[27].i |
+      | d[32].i |
+
+  # where(P.neq(traversal)) - not equal to resolved value
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_valuesXnameX_whereXneqXVXvid1X_valuesXnameXXX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().values("name").where(P.neq(__.V(vid1).values("name")))
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | vadas |
+      | lop |
+      | josh |
+      | ripple |
+      | peter |
+
+  # Empty traversal result - filters out (no match)
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_valuesXageX_whereXeqXV9999_valuesXageXXX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().values("age").where(P.eq(__.V(9999).values("age")))
+      """
+    When iterated to list
+    Then the result should be empty
+
+  # where(P.gt(traversal)) with by() modulator - compare property values
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_V_whereXgtXVXvid1X_valuesXageXXX_byXageX_valuesXnameX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V().where(P.gt(__.V(vid1).values("age"))).by("age").values("name")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | josh |
+      | peter |
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/VETraversal.feature
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/VETraversal.feature
new file mode 100644
index 0000000000..c8535d6a74
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/VETraversal.feature
@@ -0,0 +1,149 @@
+# 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.
+
+@StepClassMap @StepVertex @StepE
+Feature: Step - V(traversal) and E(traversal)
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_VXvid1X_id_VXidentityX_name
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V(vid1).id().V(__.identity()).values("name")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | marko |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_VXvid1_vid2X_id_VXidentityX_name
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And using the parameter vid2 defined as "v[josh].id"
+    And the traversal of
+      """
+      g.V(vid1, vid2).id().V(__.identity()).values("name")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | marko |
+      | josh |
+
+  # Use as()/select() to bookmark a vertex ID and return to it later via 
V(select()).
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_VXvid1X_id_asXbookmarkX_V_hasXname_joshX_VXselectXbookmarkXX_name
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      
g.V(vid1).id().as("bookmark").V().has("name","josh").V(__.select("bookmark")).values("name")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | marko |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_VXvid1X_VXoutXknowsX_idX_name
+    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 |
+
+  @GraphComputerVerificationMidENotSupported
+  Scenario: g_VXvid1X_EXoutE_idX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V(vid1).E(__.outE().id())
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | e[marko-created->lop] |
+      | e[marko-knows->josh] |
+      | e[marko-knows->vadas] |
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_injectX9999X_VXidentityX
+    Given the modern graph
+    And the traversal of
+      """
+      g.inject(9999).V(__.identity())
+      """
+    When iterated to list
+    Then the result should be empty
+
+  @GraphComputerVerificationMidENotSupported
+  Scenario: g_VXvid1X_outEXknowsX_hasXinV_name_vadasX_id_EXidentityX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      
g.V(vid1).outE("knows").filter(__.inV().has("name","vadas")).id().E(__.identity())
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | e[marko-knows->vadas] |
+
+  @GraphComputerVerificationMidENotSupported
+  Scenario: g_injectX9999X_EXidentityX
+    Given the modern graph
+    And the traversal of
+      """
+      g.inject(9999).E(__.identity())
+      """
+    When iterated to list
+    Then the result should be empty
+
+  Scenario: g_VXVXvid1X_idX_name
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V(__.V(vid1).id()).values("name")
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | marko |
+
+  Scenario: g_EXVXvid1X_outE_idX
+    Given the modern graph
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.E(__.V(vid1).outE().id())
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | e[marko-created->lop] |
+      | e[marko-knows->josh] |
+      | e[marko-knows->vadas] |
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/PropertyTraversal.feature
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/PropertyTraversal.feature
new file mode 100644
index 0000000000..8c420205bf
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/PropertyTraversal.feature
@@ -0,0 +1,253 @@
+# 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.
+
+@StepClassSideEffect @StepAddProperty
+Feature: Step - property() with traversal arguments
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_VXvid2X_propertyXalias_VXvid1X_valuesXnameXX
+    Given the empty graph
+    And the graph initializer of
+      """
+      g.addV("person").property("name","marko").property("age",29).
+        addV("person").property("name","vadas").property("age",27).
+        addV("software").property("name","lop").property("lang","java").
+        addV("person").property("name","josh").property("age",32).
+        addV("person").property("name","peter").property("age",35).
+        addV("software").property("name","ripple").property("lang","java")
+      """
+    And using the parameter vid1 defined as "v[marko].id"
+    And using the parameter vid2 defined as "v[josh].id"
+    And the traversal of
+      """
+      g.V(vid2).property("alias", __.V(vid1).values("name"))
+      """
+    When iterated to list
+    Then the result should have a count of 1
+    And the graph should return 1 for count of "g.V().has(\"alias\", 
\"marko\")"
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_VXvid1X_propertyXcreatorCount_VXvid1X_inXcreatedX_countX
+    Given the empty graph
+    And the graph initializer of
+      """
+      g.addV("person").property("name","marko").property("age",29).as("marko").
+        addV("person").property("name","vadas").property("age",27).
+        
addV("software").property("name","lop").property("lang","java").as("lop").
+        addV("person").property("name","josh").property("age",32).as("josh").
+        addV("person").property("name","peter").property("age",35).as("peter").
+        
addV("software").property("name","ripple").property("lang","java").as("ripple").
+        addE("knows").from("marko").to(__.V().has("name","vadas")).
+        addE("knows").from("marko").to("josh").
+        addE("created").from("marko").to("lop").
+        addE("created").from("josh").to("lop").
+        addE("created").from("josh").to("ripple").
+        addE("created").from("peter").to("lop")
+      """
+    And using the parameter vid1 defined as "v[lop].id"
+    And the traversal of
+      """
+      g.V(vid1).property("creatorCount", __.V(vid1).in("created").count())
+      """
+    When iterated to list
+    Then the result should have a count of 1
+    And the graph should return 1 for count of "g.V().has(\"creatorCount\", 
3L)"
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_VXvid1X_propertyXknownCount_outXknowsX_countX
+    Given the empty graph
+    And the graph initializer of
+      """
+      g.addV("person").property("name","marko").property("age",29).as("marko").
+        addV("person").property("name","vadas").property("age",27).as("vadas").
+        addV("software").property("name","lop").property("lang","java").
+        addV("person").property("name","josh").property("age",32).as("josh").
+        addV("person").property("name","peter").property("age",35).
+        addV("software").property("name","ripple").property("lang","java").
+        addE("knows").from("marko").to("vadas").
+        addE("knows").from("marko").to("josh")
+      """
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V(vid1).property("knownCount", __.out("knows").count())
+      """
+    When iterated to list
+    Then the result should have a count of 1
+    And the graph should return 1 for count of "g.V().has(\"knownCount\", 2L)"
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_VXvid1X_propertyXcreator_inXcreatedX_valuesXnameXX
+    Given the empty graph
+    And the graph initializer of
+      """
+      g.addV("person").property("name","marko").property("age",29).as("marko").
+        addV("person").property("name","vadas").property("age",27).
+        
addV("software").property("name","lop").property("lang","java").as("lop").
+        addV("person").property("name","josh").property("age",32).as("josh").
+        addV("person").property("name","peter").property("age",35).as("peter").
+        
addV("software").property("name","ripple").property("lang","java").as("ripple").
+        addE("knows").from("marko").to(__.V().has("name","vadas")).
+        addE("knows").from("marko").to("josh").
+        addE("created").from("marko").to("lop").
+        addE("created").from("josh").to("lop").
+        addE("created").from("josh").to("ripple").
+        addE("created").from("peter").to("lop")
+      """
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V(vid1).property("creator", __.in("created").values("name"))
+      """
+    When iterated to list
+    Then the result should have a count of 1
+    And the graph should return 0 for count of "g.V().has(\"creator\")"
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_VXvid2X_propertyXVXvid1X_projectXfriendCount_softwareCreatedXX
+    Given the empty graph
+    And the graph initializer of
+      """
+      g.addV("person").property("name","marko").property("age",29).as("marko").
+        addV("person").property("name","vadas").property("age",27).as("vadas").
+        
addV("software").property("name","lop").property("lang","java").as("lop").
+        addV("person").property("name","josh").property("age",32).as("josh").
+        addV("person").property("name","peter").property("age",35).as("peter").
+        
addV("software").property("name","ripple").property("lang","java").as("ripple").
+        addE("knows").from("marko").to("vadas").
+        addE("knows").from("marko").to("josh").
+        addE("created").from("marko").to("lop").
+        addE("created").from("josh").to("lop").
+        addE("created").from("josh").to("ripple").
+        addE("created").from("peter").to("lop")
+      """
+    And using the parameter vid1 defined as "v[marko].id"
+    And using the parameter vid2 defined as "v[josh].id"
+    And the traversal of
+      """
+      
g.V(vid2).property(__.V(vid1).project("friendCount","softwareCreated").by(__.out("knows").count()).by(__.out("created").values("name")))
+      """
+    When iterated to list
+    Then the result should have a count of 1
+    And the graph should return 1 for count of "g.V().has(\"friendCount\", 2L)"
+    And the graph should return 1 for count of "g.V().has(\"softwareCreated\", 
\"lop\")"
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: 
g_VXvid2X_propertyXVXvid1X_projectXoriginalName_originalLabelX_byXnameX_byXlabelXX
+    Given the empty graph
+    And the graph initializer of
+      """
+      g.addV("person").property("name","marko").property("age",29).as("marko").
+        addV("person").property("name","vadas").property("age",27).
+        
addV("software").property("name","lop").property("lang","java").as("lop").
+        addV("person").property("name","josh").property("age",32).
+        addV("person").property("name","peter").property("age",35).
+        addV("software").property("name","ripple").property("lang","java")
+      """
+    And using the parameter vid1 defined as "v[marko].id"
+    And using the parameter vid2 defined as "v[lop].id"
+    And the traversal of
+      """
+      
g.V(vid2).property(__.V(vid1).project("originalName","originalLabel").by(__.values("name")).by(__.label()))
+      """
+    When iterated to list
+    Then the result should have a count of 1
+    And the graph should return 1 for count of 
"g.V(vid2).has(\"originalName\", \"marko\")"
+    And the graph should return 1 for count of 
"g.V(vid2).has(\"originalLabel\", \"person\")"
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_VXvid1X_propertyXlist_friends_outXknowsX_valuesXnameXX
+    Given the empty graph
+    And the graph initializer of
+      """
+      g.addV("person").property("name","marko").property("age",29).as("marko").
+        addV("person").property("name","vadas").property("age",27).as("vadas").
+        addV("software").property("name","lop").property("lang","java").
+        addV("person").property("name","josh").property("age",32).as("josh").
+        addV("person").property("name","peter").property("age",35).
+        addV("software").property("name","ripple").property("lang","java").
+        addE("knows").from("marko").to("vadas").
+        addE("knows").from("marko").to("josh")
+      """
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V(vid1).property(Cardinality.list, "friends", 
__.out("knows").values("name"))
+      """
+    When iterated to list
+    Then the result should have a count of 1
+    And the graph should return 2 for count of 
"g.V(vid1).properties(\"friends\")"
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_VXvid1X_propertyXset_langs_outXcreatedX_valuesXlangXX
+    Given the empty graph
+    And the graph initializer of
+      """
+      g.addV("person").property("name","marko").property("age",29).
+        addV("person").property("name","vadas").property("age",27).
+        
addV("software").property("name","lop").property("lang","java").as("lop").
+        addV("person").property("name","josh").property("age",32).as("josh").
+        addV("person").property("name","peter").property("age",35).
+        
addV("software").property("name","ripple").property("lang","java").as("ripple").
+        addE("created").from("josh").to("lop").
+        addE("created").from("josh").to("ripple")
+      """
+    And using the parameter vid1 defined as "v[josh].id"
+    And the traversal of
+      """
+      g.V(vid1).property(Cardinality.set, "langs", 
__.out("created").values("lang"))
+      """
+    When iterated to list
+    Then the result should have a count of 1
+    And the graph should return 1 for count of 
"g.V(vid1).properties(\"langs\")"
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_VXvid1X_propertyXsingle_friend_outXknowsX_valuesXnameXX
+    Given the empty graph
+    And the graph initializer of
+      """
+      g.addV("person").property("name","marko").property("age",29).as("marko").
+        addV("person").property("name","vadas").property("age",27).as("vadas").
+        addV("software").property("name","lop").property("lang","java").
+        addV("person").property("name","josh").property("age",32).as("josh").
+        addV("person").property("name","peter").property("age",35).
+        addV("software").property("name","ripple").property("lang","java").
+        addE("knows").from("marko").to("vadas").
+        addE("knows").from("marko").to("josh")
+      """
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V(vid1).property(Cardinality.single, "friend", 
__.out("knows").values("name"))
+      """
+    When iterated to list
+    Then the traversal will raise an error
+
+  @GraphComputerVerificationMidVNotSupported
+  Scenario: g_VXvid1X_propertyXVXvid1X_groupX_byXidX_byXvaluesXnameXXX
+    Given the empty graph
+    And the graph initializer of
+      """
+      g.addV("person").property("name","marko").property("age",29)
+      """
+    And using the parameter vid1 defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V(vid1).property(__.V(vid1).group().by(__.id()).by(__.values("name")))
+      """
+    When iterated to list
+    Then the traversal will raise an error

Reply via email to