Repository: smithy-lang/smithy
Pull request #2009 (2023-10-12 18:44:17)

fix for ExampleTrait.Example equality
*Description of changes:*

Found this issue through:

```scala
val modelString =
  """|$version: "2"
     |
     |namespace foo
     |
     |@examples([
     |  {
     |    title: "Test This"
     |    input: {
     |      test: "something"
     |    }
     |  }
     |])
     |operation Foo {
     |  input := {
     |    test: String
     |  }
     |}
     |""".stripMargin

val one = Model
  .assembler()
  .addUnparsedModel("foo.smithy", modelString)
  .assemble()
  .unwrap()
val two = Model
  .assembler()
  .addUnparsedModel("foo.smithy", modelString)
  .assemble()
  .unwrap()

one == two // false
```

I've allowed edits from maintainers, feel free to change as you see fit or tell me what to change.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

YES


lewisjkl: Sorry for the noise, I should have checked prior to pushing rather than assume there wouldn't be more issues.. This time, `./gradlew :smithy-model:checkstyleMain` succeeded locally with no issues.

Patch:
diff --git a/.github/workflows/sdk-codegen-ci.yml b/.github/workflows/sdk-codegen-ci.yml
index cc8551614..69a29f3d5 100644
--- a/.github/workflows/sdk-codegen-ci.yml
+++ b/.github/workflows/sdk-codegen-ci.yml
@@ -8,7 +8,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Use Node.js 14.x
-        uses: actions/setup-node@v3
+        uses: actions/setup-node@v4
         with:
           node-version: 14.x
 
diff --git a/CHANGELOG.md b/CHANGELOG.md
index de7e10f46..dfc18065f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,27 @@
 # Smithy Changelog
 
+## 1.40.0 (2023-10-16)
+
+### Features
+
+* Added new protocol tests for the `restXml` protocol, which assert request/response behaviors for string payloads. ([#2007](https://github.com/smithy-lang/smithy/pull/2007))
+* Added new package, `smithy-smoke-test-traits`, which defines the traits for smoke tests. This package contains the smithy model definitions of said traits, their java implementations, and unit tests. ([#2005](https://github.com/smithy-lang/smithy/pull/2005))
+* Added auth-scheme validator that runs for SigV4 sub-schemes as part of the AWS rule-set in `smithy-rules-engine`. ([#2000](https://github.com/smithy-lang/smithy/pull/2000))
+* Added `AccountId` and `CredentialScope` parameters for AWS-specific endpoint rules in `smithy-rules-engine`. ([#1993](https://github.com/smithy-lang/smithy/pull/1993))
+
+### Documentation
+
+* Added traits anchors for a few traits that were previously missing. ([#2008](https://github.com/smithy-lang/smithy/pull/2008))
+* Added `Smithy Examples` embedding to the [smithy.io](https://smithy.io) sidebar under `Project`. ([#2006](https://github.com/smithy-lang/smithy/pull/2006))
+* Added important notice for the `@contextParam` trait to clarify expected behavior of clients when `@required` is used on the same member.([#1999](https://github.com/smithy-lang/smithy/pull/1999))
+
+### Bug Fixes
+
+* Fixed missing source-locations in emitted events by `smithy-diff`. Previously, `N/A` would be displayed instead of the real location. ([#2001](https://github.com/smithy-lang/smithy/pull/2001))
+* Added missing method override in `smithy-rules-engine`. ([#1998](https://github.com/smithy-lang/smithy/pull/1998))
+* Fixed bug where properties of resource shapes were not being serialized in the IDL serializer. ([#1996](https://github.com/smithy-lang/smithy/pull/1996))
+* Fixed an issue with OpenAPI conversion not allowing multiple errors for a single status code with an opt-in that uses `oneOf` to de-conflict the errors. ([#1995](https://github.com/smithy-lang/smithy/pull/1995))
+
 ## 1.39.1 (2023-09-26)
 
 ### Bug Fixes
diff --git a/VERSION b/VERSION
index 0c11aad2f..32b7211cb 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-1.39.1
+1.40.0
diff --git a/docs/source-2.0/aws/aws-endpoints-region.rst b/docs/source-2.0/aws/aws-endpoints-region.rst
new file mode 100644
index 000000000..10c0db91f
--- /dev/null
+++ b/docs/source-2.0/aws/aws-endpoints-region.rst
@@ -0,0 +1,414 @@
+.. _aws-endpoints:
+
+===============================
+AWS Declarative Endpoint Traits
+===============================
+
+This document defines AWS declarative endpoint traits.
+
+.. _aws-endpoints-overview:
+
+----------------------
+AWS Endpoints Overview
+----------------------
+
+An endpoint is used to connect programmatically to an AWS service. An endpoint is the URL of the
+entry point for an AWS web service.
+
+Most AWS services are regional: they offer regional endpoints and the service's resources are independent
+of similar resources in other regions.
+
+.. _aws-endpoints-region:
+
+Region
+    Each `region <https://docs.aws.amazon.com/whitepapers/latest/aws-fault-isolation-boundaries/regions.html>`_
+    consists of multiple availability zones within a single geographic area. Regions themselves are isolated
+    and independent from other regions.
+
+.. _aws-partition:
+
+Partition
+    AWS groups regions into
+    `partitions <https://docs.aws.amazon.com/whitepapers/latest/aws-fault-isolation-boundaries/partitions.html>`_.
+    Every region is in exactly one partition and each partition has one or more regions.
+    AWS commercial Regions are in the ``aws`` partition, Regions in China are in the ``aws-cn`` partition,
+    and AWS GovCloud Regions are in the ``aws-us-gov`` partition.
+
+.. _fips-endpoints:
+
+FIPS Endpoints
+    Some AWS services have endpoints that support Federal Information Processing Standard (FIPS) 140-2.
+    For a list of FIPS endpoints, see
+    `FIPS endpoints by Service
+    <http://aws.amazon.com/compliance/fips/#FIPS_Endpoints_by_Service>`_.
+
+.. _dualstack-endpoints:
+
+Dual stack Endpoints
+    Some AWS services offer dual stack endpoints, so that you can access them using either IPv4 or IPv6 requests.
+    For a list of services that support dual stack endpoints, see `AWS services that support IPv6
+    <https://docs.aws.amazon.com/vpc/latest/userguide/aws-ipv6-support.html>`_.
+
+.. smithy-trait:: aws.endpoints#endpointsModifier
+.. _aws.endpoints#endpointsModifier-trait:
+
+-----------------------------------------
+``aws.endpoints#endpointsModifier`` trait
+-----------------------------------------
+
+Summary
+    A meta-trait that marks a trait as an endpoint modifier.  Traits that are marked with this trait are
+    applied to service shapes or operation shapes to indicate how a client can resolve
+    endpoints for that service or operation.
+Trait selector
+    ``[trait|trait]``
+Value type
+    Annotation trait
+
+The following example defines a service with ``standardRegionalEndpoints`` modified by
+the hypothetical ``fooExample`` endpoint modifier.
+
+.. code-block:: smithy
+
+    $version: "2"
+
+    namespace smithy.example
+
+    use aws.endpoints#endpointsModifier
+    use aws.endpoints#standardRegionalEndpoints
+
+    @endpointsModifier
+    @trait(selector: "service")
+    structure fooExample {}
+
+    @standardRegionalEndpoints
+    @fooExample
+    service MyService {
+        version: "2020-04-02"
+    }
+
+Because endpoint modification definitions are just specialized shapes, they
+can also support configuration settings.
+
+.. code-block:: smithy
+
+    $version: "2"
+    namespace smithy.example
+
+    use aws.endpoints#endpointsModifier
+    use aws.endpoints#standardRegionalEndpoints
+
+    @endpointsModifier
+    @trait(selector: "service")
+    structure endpointSuffix {
+        suffix: String
+    }
+
+    @standardRegionalEndpoints
+    @endpointSuffix(suffix="-suffix")
+    service MyService {
+        version: "2020-04-02"
+    }
+
+
+
+.. smithy-trait:: aws.endpoints#standardRegionalEndpoints
+.. _aws.endpoints#standardRegionalEndpoints-trait:
+
+-------------------------------------------------
+``aws.endpoints#standardRegionalEndpoints`` trait
+-------------------------------------------------
+
+Summary
+    An :ref:`endpoints modifier trait <aws.endpoints#endpointsModifier-trait>`
+    that indicates that a service's endpoints should be resolved using the standard AWS regional
+    patterns:
+
+    - Default: ``{service}.{region}.{dnsSuffix}``
+    - Fips: ``{service}-fips.{region}.{dnsSuffix}``
+    - Dualstack: ``{service}.{region}.{dualStackDnsSuffix}``
+    - Fips/Dualstack: ``{service}-fips.{region}.{dualStackDnsSuffix}``
+
+Trait selector
+    ``service``
+Trait value
+    A ``structure`` with the following properties:
+
+.. list-table::
+    :header-rows: 1
+    :widths: 10 30 60
+
+    * - Property
+      - Type
+      - Description
+    * - partitionSpecialCases
+      - ``map`` of partition to `PartitionSpecialCase object`_
+      - A map of partition to partition special cases - endpoints for a partition that do not follow the
+        standard patterns.
+    * - regionSpecialCases
+      - ``map`` of region to `RegionSpecialCase object`_
+      - A map of region to regional special cases - endpoints for a region that do not follow the
+        standard patterns.
+
+Conflicts with
+    :ref:`aws.endpoints#standardPartitionalEndpoints-trait`
+
+Most AWS services are regionalized and are strongly encouraged to follow
+the standard endpoint patterns defined above for consistency, and to
+ensure that endpoints are forwards compatible, and that SDK updates are
+not required when the service launches in a new region or partition.
+
+The following example defines a service that uses the standard regional endpoints:
+
+.. code-block:: smithy
+
+    $version: "2"
+
+    namespace smithy.example
+
+    use aws.endpoints#standardRegionalEndpoints
+
+    @standardRegionalEndpoints
+    service MyService {
+        version: "2020-04-02"
+    }
+
+While services are strongly encouraged to follow standard endpoint patterns,
+there are occasional exceptions and special cases.  The following example defines
+a service that uses standard regional endpoints, but uses a non-standard pattern for
+FIPS endpoints in US GovCloud:
+
+.. code-block:: smithy
+
+    @standardRegionalEndpoints(
+        partitionSpecialCases: {
+            aws-us-gov: [
+                {
+                    endpoint: "myservice.{region}.{dnsSuffix}",
+                    fips: true
+                }
+            ]
+        }
+    )
+    service MyService {
+        version: "2020-04-02"
+    }
+
+``PartitionSpecialCase`` object
+-------------------------------
+
+A ``PartitionSpecialCase`` defines the endpoint pattern to apply for all regional endpoints
+in the given partition. A PartitionSpecialCase object contains the following properties:
+
+.. list-table::
+    :header-rows: 1
+    :widths: 10 30 60
+
+    * - Property name
+      - Type
+      - Description
+    * - endpoint
+      - ``string``
+      - **Required**. The special-cased endpoint template.
+    * - dualStack
+      - ``boolean``
+      - When ``true`` the special case will apply to dualstack endpoint variants.
+    * - fips
+      - ``boolean``
+      - When ``true`` the special case will apply to fips endpoint variants.
+
+
+``RegionSpecialCase`` object
+----------------------------
+
+A ``RegionSpecialCase`` object contains the following properties:
+
+.. list-table::
+    :header-rows: 1
+    :widths: 10 30 60
+
+    * - Property name
+      - Type
+      - Description
+    * - endpoint
+      - ``string``
+      - **Required**. The special-cased endpoint template.
+    * - dualStack
+      - ``boolean``
+      - When ``true`` the special case will apply to dualstack endpoint variants.
+    * - fips
+      - ``boolean``
+      - When ``true`` the special case will apply to fips endpoint variants.
+    * - signingRegion
+      - ``string``
+      - Overrides the signingRegion used for this region.
+
+.. smithy-trait:: aws.endpoints#standardPartitionalEndpoints
+.. _aws.endpoints#standardPartitionalEndpoints-trait:
+
+----------------------------------------------------
+``aws.endpoints#standardPartitionalEndpoints`` trait
+----------------------------------------------------
+
+Summary
+    An :ref:`endpoints modifier trait <aws.endpoints#endpointsModifier-trait>`
+    that indicates that a service is
+    `partitional <https://docs.aws.amazon.com/whitepapers/latest/aws-fault-isolation-boundaries/global-services.html#global-services-that-are-unique-by-partition>`_
+    and a single endpoint should be resolved per partition.
+Trait selector
+    ``service``
+Trait value
+    A ``structure`` with the following properties:
+
+.. list-table::
+    :header-rows: 1
+    :widths: 10 30 60
+
+    * - Property
+      - Type
+      - Description
+    * - endpointPatternType
+      - ``string``
+      - **Required** The pattern type to use for the partition endpoint.  This value can be set to ``service_dnsSuffix`` to
+        use the ``{service}.{dnsSuffix}`` pattern or ``service_region_dnsSuffix`` to use
+        ``{service}.{region}.{dnsSuffix}``.
+    * - partitionEndpointSpecialCases
+      - ``map`` of partition to `PartitionEndpointSpecialCase object`_
+      - A map of partition to partition endpoint special cases - partitions that do not follow the
+        service's standard patterns or are located in a region other than the partition's
+        ``defaultGlobalRegion``.
+
+Conflicts with
+    :ref:`aws.endpoints#standardRegionalEndpoints-trait`
+
+Partitional services (also known as "global" services) resolve a single endpoint per partition.
+That single endpoint is located in the partition's ``defaultGlobalRegion``. Partitional
+services should follow one of two standard patterns:
+
+- ``service_dnsSuffix``: ``{service}.{dnsSuffix}``
+- ``service_region_dnsSuffix``: ``{service}.{region}.{dnsSuffix}``
+
+The following example defines a partitional service that uses ``{service}.{dnsSuffix}``:
+
+.. code-block:: smithy
+
+    $version: "2"
+
+    namespace smithy.example
+
+    use aws.endpoints#standardPartitionalEndpoints
+
+    @standardPartitionalEndpoints(endpointPatternType: "service_dnsSuffix")
+    service MyService {
+        version: "2020-04-02"
+    }
+
+Services should follow the standard patterns; however, occasionally there are special cases.
+The following example defines a partitional service that uses a special case pattern in
+the ``aws`` partition and uses a non-standard global region in the ``aws-cn`` partition:
+
+.. code-block:: smithy
+
+    @standardPartitionalEndpoints(
+        endpointPatternType: "service_dnsSuffix",
+        partitionEndpointSpecialCases: {
+            aws: [{endpoint: "myservice.global.amazonaws.com"}],
+            aws-cn: [{region: "cn-north-1"}]
+        }
+    )
+    service MyService {
+        version: "2020-04-02"
+    }
+
+``PartitionEndpointSpecialCase`` object
+---------------------------------------
+
+A ``PartitionEndpointSpecialCase`` object contains the following properties:
+
+.. list-table::
+    :header-rows: 1
+    :widths: 10 30 60
+
+    * - Property name
+      - Type
+      - Description
+    * - endpoint
+      - ``string``
+      - The special-cased endpoint template.
+    * - region
+      - ``string``
+      - Override the ``defaultGlobalRegion`` used in this partition.
+    * - dualStack
+      - ``boolean``
+      - When ``true`` the special case will apply to dualstack endpoint variants.
+    * - fips
+      - ``boolean``
+      - When ``true`` the special case will apply to fips endpoint variants.
+
+.. smithy-trait:: aws.endpoints#dualStackOnlyEndpoints
+.. _aws.endpoints#dualStackOnlyEndpoints-trait:
+
+----------------------------------------------
+``aws.endpoints#dualStackOnlyEndpoints`` trait
+----------------------------------------------
+
+Summary
+    An :ref:`endpoints modifier trait <aws.endpoints#endpointsModifier-trait>`
+    that indicates that a service has only
+    `dual stack endpoints <https://docs.aws.amazon.com/general/latest/gr/rande.html#dual-stack-endpoints>`_,
+    does not support IPV4 only endpoints, and should not have the ``useDualStackEndpoint`` endpoint parameter.
+    Dual stack endpoints support IPV4 and IPV6.
+Trait selector
+    ``service``
+Trait value
+    Annotation trait
+
+Adding the ``dualStackOnlyEndpoints`` trait to a service modifies the generation of endpoints from
+:ref:`aws.endpoints#standardRegionalEndpoints-trait` or :ref:`aws.endpoints#standardPartitionalEndpoints-trait`,
+removes the ``useDualStackEndpoint`` parameter, and defaults the behavior to dual stack for
+all partitions that support it.
+
+The following example specifies a service that uses standard regional endpoint patterns and
+is dual stack only:
+
+.. code-block:: smithy
+
+     @standardRegionalEndpoints
+     @dualStackOnlyEndpoints
+     service MyService {
+         version: "2020-04-02"
+     }
+
+.. smithy-trait:: aws.endpoints#rulesBasedEndpoints
+.. _aws.endpoints#rulesBasedEndpoints-trait:
+
+-------------------------------------------
+``aws.endpoints#rulesBasedEndpoints`` trait
+-------------------------------------------
+
+Summary
+    An :ref:`endpoints modifier trait <aws.endpoints#endpointsModifier-trait>`
+    that indicates that a service has hand written endpoint rules.
+Trait selector
+    ``service``
+Trait value
+    Annotation trait
+
+Services marked with the ``rulesBasedEndpoints`` trait have hand written endpoint rules that
+extend or replace their standard generated endpoint rules through an external mechanism.
+This trait marks the presence of hand written rules, which are added to the model by a transformer,
+but does not specify their behavior.
+
+A service with ``rulesBasedEndpoints`` may extend the functionality of
+endpoint behavior described in the model through other
+:ref:`endpoints modifier traits <aws.endpoints#endpointsModifier-trait>`
+by modifying the generated :ref:`EndpointRuleSet <smithy.rules#endpointRuleSet-trait>`.
+The following example specifies a service that has standard regional endpoints extended with hand written rules:
+
+.. code-block:: smithy
+
+     @standardRegionalEndpoints
+     @rulesBasedEndpoints
+     service MyService {
+         version: "2020-04-02"
+     }
diff --git a/docs/source-2.0/aws/index.rst b/docs/source-2.0/aws/index.rst
index d8c49590d..ae9e5711c 100644
--- a/docs/source-2.0/aws/index.rst
+++ b/docs/source-2.0/aws/index.rst
@@ -7,6 +7,7 @@ AWS integrations
 
     aws-core
     aws-auth
+    aws-endpoints-region
     aws-iam
     amazon-apigateway
     aws-cloudformation
diff --git a/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/DualStackOnlyEndpointsTrait.java b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/DualStackOnlyEndpointsTrait.java
new file mode 100644
index 000000000..e0d308484
--- /dev/null
+++ b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/DualStackOnlyEndpointsTrait.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.rulesengine.aws.traits;
+
+import software.amazon.smithy.model.node.Node;
+import software.amazon.smithy.model.node.ObjectNode;
+import software.amazon.smithy.model.shapes.ShapeId;
+import software.amazon.smithy.model.traits.AnnotationTrait;
+
+/**
+ * An endpoints modifier trait that indicates a service has only dual stack endpoints,
+ * does not support IPV4 only endpoints, and should not have the useDualStackEndpoint endpoint parameter.
+ */
+public final class DualStackOnlyEndpointsTrait extends AnnotationTrait {
+    public static final ShapeId ID = ShapeId.from("aws.endpoints#dualStackOnlyEndpoints");
+
+    public DualStackOnlyEndpointsTrait(ObjectNode node) {
+        super(ID, node);
+    }
+
+    public DualStackOnlyEndpointsTrait() {
+        this(Node.objectNode());
+    }
+
+    public static final class Provider extends AnnotationTrait.Provider<DualStackOnlyEndpointsTrait> {
+        public Provider() {
+            super(ID, DualStackOnlyEndpointsTrait::new);
+        }
+    }
+}
diff --git a/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/EndpointModifierIndex.java b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/EndpointModifierIndex.java
new file mode 100644
index 000000000..9f65c647c
--- /dev/null
+++ b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/EndpointModifierIndex.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.rulesengine.aws.traits;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+import software.amazon.smithy.model.Model;
+import software.amazon.smithy.model.knowledge.KnowledgeIndex;
+import software.amazon.smithy.model.shapes.ServiceShape;
+import software.amazon.smithy.model.shapes.Shape;
+import software.amazon.smithy.model.shapes.ShapeId;
+import software.amazon.smithy.model.shapes.ToShapeId;
+import software.amazon.smithy.model.traits.Trait;
+
+/**
+ * Locates the endpoint modifier traits applied to services.
+ *
+ * Endpoint modifier traits are traits that are marked by {@link EndpointModifierTrait}
+ */
+public final class EndpointModifierIndex implements KnowledgeIndex {
+
+    private final Map<ShapeId, Map<ShapeId, Trait>> endpointModifierTraits = new HashMap<>();
+
+    public EndpointModifierIndex(Model model) {
+        for (ServiceShape serviceShape : model.getServiceShapes()) {
+            Map<ShapeId, Trait> result = new TreeMap<>();
+            for (Trait trait : serviceShape.getAllTraits().values()) {
+                Shape traitShape = model.getShape(trait.toShapeId()).get();
+                if (traitShape.hasTrait(EndpointModifierTrait.ID)) {
+                    result.put(trait.toShapeId(), trait);
+                }
+            }
+            endpointModifierTraits.put(serviceShape.toShapeId(), result);
+        }
+    }
+
+    public static EndpointModifierIndex of(Model model) {
+        return model.getKnowledge(EndpointModifierIndex.class, EndpointModifierIndex::new);
+    }
+
+    /**
+     * Gets all endpoint modifier traits applied to a service.
+     *
+     * @param toShapeId Service shape to query
+     * @return Map of endpoint modifier trait ID to the trait
+     */
+    public Map<ShapeId, Trait> getEndpointModifierTraits(ToShapeId toShapeId) {
+        return endpointModifierTraits.get(toShapeId);
+    }
+}
diff --git a/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/EndpointModifierTrait.java b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/EndpointModifierTrait.java
new file mode 100644
index 000000000..1ee4e401d
--- /dev/null
+++ b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/EndpointModifierTrait.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.rulesengine.aws.traits;
+
+import software.amazon.smithy.model.node.Node;
+import software.amazon.smithy.model.node.ObjectNode;
+import software.amazon.smithy.model.shapes.ShapeId;
+import software.amazon.smithy.model.traits.AnnotationTrait;
+
+/**
+ *  A meta-trait that marks a trait as an endpoint modifier.
+ *
+ *  Traits that are marked with this trait are applied to service shapes or operation shapes to
+ *  indicate how a client can resolve endpoints for that service or operation.
+ */
+public final class EndpointModifierTrait extends AnnotationTrait {
+    public static final ShapeId ID = ShapeId.from("aws.endpoints#endpointsModifier");
+
+    public EndpointModifierTrait(ObjectNode node) {
+        super(ID, node);
+    }
+
+    public EndpointModifierTrait() {
+        this(Node.objectNode());
+    }
+
+    public static final class Provider extends AnnotationTrait.Provider<EndpointModifierTrait> {
+        public Provider() {
+            super(ID, EndpointModifierTrait::new);
+        }
+    }
+}
diff --git a/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/EndpointPatternType.java b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/EndpointPatternType.java
new file mode 100644
index 000000000..f836f7e15
--- /dev/null
+++ b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/EndpointPatternType.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.rulesengine.aws.traits;
+
+import software.amazon.smithy.model.node.Node;
+import software.amazon.smithy.model.node.StringNode;
+
+/**
+ * The pattern type to use for the partitional services.
+ */
+public enum EndpointPatternType {
+    /** An endpoint with pattern `{service}.{dnsSuffix}`.*/
+    SERVICE_DNSSUFFIX("service_dnsSuffix"),
+
+    /** An endpoint with pattern `{service}.{region}.{dnsSuffix}`. */
+    SERVICE_REGION_DNSSUFFI("service_region_dnsSuffix");
+
+    private final String name;
+
+    EndpointPatternType(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Gets the name of a partitional service pattern type.
+     *
+     * @return Returns a partitional service pattern type name.
+     */
+    public String getName() {
+        return name;
+    }
+
+    public static EndpointPatternType fromNode(Node node) {
+        StringNode value = node.expectStringNode();
+        for (EndpointPatternType type: EndpointPatternType.values()) {
+            if (type.name.equals(value.getValue())) {
+                return type;
+            }
+        }
+        throw new RuntimeException(String.format(
+            "Unable to find EndpointPatternType enum with value [%s]", value.getValue()));
+    }
+}
diff --git a/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/PartitionEndpointSpecialCase.java b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/PartitionEndpointSpecialCase.java
new file mode 100644
index 000000000..3adaefb13
--- /dev/null
+++ b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/PartitionEndpointSpecialCase.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.rulesengine.aws.traits;
+
+import java.util.Objects;
+import software.amazon.smithy.model.FromSourceLocation;
+import software.amazon.smithy.model.SourceLocation;
+import software.amazon.smithy.model.node.Node;
+import software.amazon.smithy.model.node.ObjectNode;
+import software.amazon.smithy.model.node.ToNode;
+import software.amazon.smithy.utils.SmithyBuilder;
+import software.amazon.smithy.utils.ToSmithyBuilder;
+
+/**
+ * A special case that does not follow the services standard patterns
+ * or are located in a region other than the partition's default global region.
+ */
+public final class PartitionEndpointSpecialCase
+    implements FromSourceLocation, ToNode, ToSmithyBuilder<PartitionEndpointSpecialCase> {
+
+    private static final String ENDPOINT = "endpoint";
+    private static final String REGION = "region";
+    private static final String DUAL_STACK = "dualStack";
+    private static final String FIPS = "fips";
+    private final String endpoint;
+    private final String region;
+    private final Boolean dualStack;
+    private final Boolean fips;
+    private final SourceLocation sourceLocation;
+
+    private PartitionEndpointSpecialCase(Builder builder) {
+        this.endpoint = builder.endpoint;
+        this.region = builder.region;
+        this.dualStack = builder.dualStack;
+        this.fips = builder.fips;
+        this.sourceLocation = Objects.requireNonNull(builder.sourceLocation);
+    }
+
+    /**
+     * Gets the endpoint.
+     *
+     * @return Returns the endpoint
+     */
+    public String getEndpoint() {
+        return endpoint;
+    }
+
+    /**
+     * Gets the dualStack.
+     *
+     * @return Returns the dualStack
+     */
+    public Boolean getDualStack() {
+        return dualStack;
+    }
+
+    /**
+     * Gets the fips.
+     *
+     * @return Returns the fips
+     */
+    public Boolean getFips() {
+        return fips;
+    }
+
+    /**
+     * Gets the region.
+     *
+     * @return Returns the region
+     */
+    public String getRegion() {
+        return region;
+    }
+
+    @Override
+    public Node toNode() {
+        return Node.objectNodeBuilder()
+            .withMember(ENDPOINT, endpoint)
+            .withMember(REGION, region)
+            .withMember(DUAL_STACK, dualStack.toString())
+            .withMember(FIPS, fips.toString())
+            .build();
+    }
+
+    @Override
+    public SmithyBuilder<PartitionEndpointSpecialCase> toBuilder() {
+        return new Builder()
+            .endpoint(endpoint)
+            .region(region)
+            .dualStack(dualStack)
+            .fips(fips)
+            .sourceLocation(sourceLocation);
+    }
+
+    @Override
+    public SourceLocation getSourceLocation() {
+        return FromSourceLocation.super.getSourceLocation();
+    }
+
+    /**
+     * Creates a {@link PartitionEndpointSpecialCase} instance from the given Node information.
+     *
+     * @param node the node to deserialize.
+     * @return Returns a PartitionEndpointSpecialCase
+     */
+    public static PartitionEndpointSpecialCase fromNode(Node node) {
+        ObjectNode objectNode = node.expectObjectNode();
+        return builder()
+            .sourceLocation(objectNode.getSourceLocation())
+            .endpoint(objectNode.expectStringMember(ENDPOINT).getValue())
+            .region(objectNode.expectStringMember(REGION).getValue())
+            .dualStack(objectNode.getBooleanMemberOrDefault(DUAL_STACK, null))
+            .fips(objectNode.getBooleanMemberOrDefault(FIPS, null))
+            .build();
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static final class Builder implements SmithyBuilder<PartitionEndpointSpecialCase> {
+        private String endpoint;
+        private String region;
+        private Boolean dualStack;
+        private Boolean fips;
+        private SourceLocation sourceLocation = SourceLocation.none();
+
+        @Override
+        public PartitionEndpointSpecialCase build() {
+            return new PartitionEndpointSpecialCase(this);
+        }
+
+        /**
+         * Sets the special case endpoint template.
+         *
+         * @param endpoint Special case endpoint template to set.
+         * @return Returns the builder.
+         */
+        public Builder endpoint(String endpoint) {
+            this.endpoint = endpoint;
+            return this;
+        }
+
+        /**
+         * Sets the dualstack.
+         *
+         * @param dualStack dualstack to set.
+         * @return Returns the builder.
+         */
+        public Builder dualStack(Boolean dualStack) {
+            this.dualStack = dualStack;
+            return this;
+        }
+
+        /**
+         * Sets the fips.
+         *
+         * @param fips fips to set.
+         * @return Returns the builder.
+         */
+        public Builder fips(Boolean fips) {
+            this.fips = fips;
+            return this;
+        }
+
+        /**
+         * Sets the region.
+         *
+         * @param region region to set.
+         * @return Returns the builder.
+         */
+        public Builder region(String region) {
+            this.region = region;
+            return this;
+        }
+
+        Builder sourceLocation(SourceLocation sourceLocation) {
+            this.sourceLocation = sourceLocation;
+            return this;
+        }
+    }
+}
diff --git a/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/PartitionSpecialCase.java b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/PartitionSpecialCase.java
new file mode 100644
index 000000000..857e74c5f
--- /dev/null
+++ b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/PartitionSpecialCase.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.rulesengine.aws.traits;
+
+import java.util.Objects;
+import software.amazon.smithy.model.FromSourceLocation;
+import software.amazon.smithy.model.SourceLocation;
+import software.amazon.smithy.model.node.Node;
+import software.amazon.smithy.model.node.ObjectNode;
+import software.amazon.smithy.model.node.ToNode;
+import software.amazon.smithy.utils.SmithyBuilder;
+import software.amazon.smithy.utils.ToSmithyBuilder;
+
+/**
+ * A special case where endpoints for a partition that do not follow the standard patterns.
+ */
+public final class PartitionSpecialCase implements FromSourceLocation, ToNode, ToSmithyBuilder<PartitionSpecialCase> {
+
+    private static final String ENDPOINT = "endpoint";
+    private static final String DUAL_STACK = "dualStack";
+    private static final String FIPS = "fips";
+    private final String endpoint;
+    private final Boolean dualStack;
+    private final Boolean fips;
+    private final SourceLocation sourceLocation;
+
+    private PartitionSpecialCase(Builder builder) {
+        this.endpoint = builder.endpoint;
+        this.dualStack = builder.dualStack;
+        this.fips = builder.fips;
+        this.sourceLocation = Objects.requireNonNull(builder.sourceLocation);
+    }
+
+    /**
+     * Gets the endpoint.
+     *
+     * @return Returns the endpoint
+     */
+    public String getEndpoint() {
+        return endpoint;
+    }
+
+    /**
+     * Gets the dualStack.
+     *
+     * @return Returns the dualStack
+     */
+    public Boolean getDualStack() {
+        return dualStack;
+    }
+
+    /**
+     * Gets the fips.
+     *
+     * @return Returns the fips
+     */
+    public Boolean getFips() {
+        return fips;
+    }
+
+    @Override
+    public Node toNode() {
+        return Node.objectNodeBuilder()
+            .withMember(ENDPOINT, endpoint)
+            .withMember(DUAL_STACK, dualStack.toString())
+            .withMember(FIPS, fips.toString())
+            .build();
+    }
+
+    @Override
+    public SmithyBuilder<PartitionSpecialCase> toBuilder() {
+        return new Builder()
+            .dualStack(dualStack)
+            .endpoint(endpoint)
+            .fips(fips)
+            .sourceLocation(sourceLocation);
+    }
+
+    @Override
+    public SourceLocation getSourceLocation() {
+        return FromSourceLocation.super.getSourceLocation();
+    }
+
+    /**
+     * Creates a {@link PartitionSpecialCase} instance from the given Node information.
+     *
+     * @param node the node to deserialize.
+     * @return Returns a PartitionSpecialCase
+     */
+    public static PartitionSpecialCase fromNode(Node node) {
+        ObjectNode objectNode = node.expectObjectNode();
+        return builder()
+            .sourceLocation(objectNode.getSourceLocation())
+            .endpoint(objectNode.expectStringMember(ENDPOINT).getValue())
+            .dualStack(objectNode.getBooleanMemberOrDefault(DUAL_STACK, null))
+            .fips(objectNode.getBooleanMemberOrDefault(FIPS, null))
+            .build();
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static final class Builder implements SmithyBuilder<PartitionSpecialCase> {
+        private String endpoint;
+        private Boolean dualStack;
+        private Boolean fips;
+        private SourceLocation sourceLocation = SourceLocation.none();
+
+        @Override
+        public PartitionSpecialCase build() {
+            return new PartitionSpecialCase(this);
+        }
+
+        /**
+         * Sets the special case endpoint template.
+         *
+         * @param endpoint Special case endpoint template to set.
+         * @return Returns the builder.
+         */
+        public Builder endpoint(String endpoint) {
+            this.endpoint = endpoint;
+            return this;
+        }
+
+        /**
+         * Sets the dualstack.
+         *
+         * @param dualStack dualstack to set.
+         * @return Returns the builder.
+         */
+        public Builder dualStack(Boolean dualStack) {
+            this.dualStack = dualStack;
+            return this;
+        }
+
+        /**
+         * Sets the fips.
+         *
+         * @param fips fips to set.
+         * @return Returns the builder.
+         */
+        public Builder fips(Boolean fips) {
+            this.fips = fips;
+            return this;
+        }
+
+        Builder sourceLocation(SourceLocation sourceLocation) {
+            this.sourceLocation = sourceLocation;
+            return this;
+        }
+
+    }
+}
diff --git a/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/RegionSpecialCase.java b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/RegionSpecialCase.java
new file mode 100644
index 000000000..742c551ff
--- /dev/null
+++ b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/RegionSpecialCase.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.rulesengine.aws.traits;
+
+import java.util.Objects;
+import software.amazon.smithy.model.FromSourceLocation;
+import software.amazon.smithy.model.SourceLocation;
+import software.amazon.smithy.model.node.Node;
+import software.amazon.smithy.model.node.ObjectNode;
+import software.amazon.smithy.model.node.ToNode;
+import software.amazon.smithy.utils.SmithyBuilder;
+import software.amazon.smithy.utils.ToSmithyBuilder;
+
+/**
+ * A special case where endpoints for a region that do not follow the standard patterns.
+ */
+public final class RegionSpecialCase implements FromSourceLocation, ToNode, ToSmithyBuilder<RegionSpecialCase> {
+    private static final String ENDPOINT = "endpoint";
+    private static final String DUAL_STACK = "dualStack";
+    private static final String FIPS = "fips";
+    private static final String SIGNING_REGION = "signingRegion";
+    private final String endpoint;
+    private final Boolean dualStack;
+    private final Boolean fips;
+    private final String signingRegion;
+    private final SourceLocation sourceLocation;
+
+    private RegionSpecialCase(Builder builder) {
+        this.endpoint = builder.endpoint;
+        this.dualStack = builder.dualStack;
+        this.fips = builder.fips;
+        this.signingRegion = builder.signingRegion;
+        this.sourceLocation = Objects.requireNonNull(builder.sourceLocation);
+    }
+
+    /**
+     * Gets the endpoint.
+     *
+     * @return Returns the endpoint
+     */
+    public String getEndpoint() {
+        return endpoint;
+    }
+
+    /**
+     * Gets the dualStack.
+     *
+     * @return Returns the dualStack
+     */
+    public Boolean getDualStack() {
+        return dualStack;
+    }
+
+    /**
+     * Gets the fips.
+     *
+     * @return Returns the fips
+     */
+    public Boolean getFips() {
+        return fips;
+    }
+
+    /**
+     * Gets the signing region.
+     *
+     * @return Returns the signing region
+     */
+    public String getSigningRegion() {
+        return signingRegion;
+    }
+
+    @Override
+    public Node toNode() {
+        return Node.objectNodeBuilder()
+            .withMember(ENDPOINT, endpoint)
+            .withMember(DUAL_STACK, dualStack.toString())
+            .withMember(FIPS, fips.toString())
+            .withMember(SIGNING_REGION, signingRegion)
+            .build();
+    }
+
+    @Override
+    public SmithyBuilder<RegionSpecialCase> toBuilder() {
+        return new Builder()
+            .dualStack(dualStack)
+            .endpoint(endpoint)
+            .fips(fips)
+            .signingRegion(signingRegion)
+            .sourceLocation(sourceLocation);
+    }
+
+    @Override
+    public SourceLocation getSourceLocation() {
+        return FromSourceLocation.super.getSourceLocation();
+    }
+
+    /**
+     * Creates a {@link RegionSpecialCase} instance from the given Node information.
+     *
+     * @param node the node to deserialize.
+     * @return Returns a RegionSpecialCase
+     */
+    public static RegionSpecialCase fromNode(Node node) {
+        ObjectNode objectNode = node.expectObjectNode();
+        return builder()
+            .sourceLocation(objectNode.getSourceLocation())
+            .endpoint(objectNode.expectStringMember(ENDPOINT).getValue())
+            .dualStack(objectNode.getBooleanMemberOrDefault(DUAL_STACK, null))
+            .fips(objectNode.getBooleanMemberOrDefault(FIPS, null))
+            .signingRegion(objectNode.getStringMemberOrDefault(SIGNING_REGION, null))
+            .build();
+    }
+
+    public static Builder builder() {
+        return new RegionSpecialCase.Builder();
+    }
+
+    public static final class Builder implements SmithyBuilder<RegionSpecialCase> {
+        private String endpoint;
+        private Boolean dualStack;
+        private Boolean fips;
+        private String signingRegion;
+        private SourceLocation sourceLocation = SourceLocation.none();
+
+        @Override
+        public RegionSpecialCase build() {
+            return new RegionSpecialCase(this);
+        }
+
+        /**
+         * Sets the special case endpoint template.
+         *
+         * @param endpoint Special case endpoint template to set.
+         * @return Returns the builder.
+         */
+        public Builder endpoint(String endpoint) {
+            this.endpoint = Objects.requireNonNull(endpoint);
+            return this;
+        }
+
+        /**
+         * Sets the dualstack.
+         *
+         * @param dualStack dualstack to set.
+         * @return Returns the builder.
+         */
+        public Builder dualStack(Boolean dualStack) {
+            this.dualStack = dualStack;
+            return this;
+        }
+
+        /**
+         * Sets the fips.
+         *
+         * @param fips fips to set.
+         * @return Returns the builder.
+         */
+        public Builder fips(Boolean fips) {
+            this.fips = fips;
+            return this;
+        }
+
+        /**
+         * Sets the signing region.
+         *
+         * @param signingRegion region to set.
+         * @return Returns the builder.
+         */
+        public Builder signingRegion(String signingRegion) {
+            this.signingRegion = signingRegion;
+            return this;
+        }
+
+        public Builder sourceLocation(SourceLocation sourceLocation) {
+            this.sourceLocation = sourceLocation;
+            return this;
+        }
+
+    }
+}
diff --git a/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/RuleBasedEndpointsTrait.java b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/RuleBasedEndpointsTrait.java
new file mode 100644
index 000000000..2f050e3d5
--- /dev/null
+++ b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/RuleBasedEndpointsTrait.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.rulesengine.aws.traits;
+
+import software.amazon.smithy.model.node.Node;
+import software.amazon.smithy.model.node.ObjectNode;
+import software.amazon.smithy.model.shapes.ShapeId;
+import software.amazon.smithy.model.traits.AnnotationTrait;
+
+/**
+ * A trait that indicates that a service has handwritten endpoint rules.
+ *
+ * Services marked with this trait have handwritten endpoint rules that
+ * extend or replace their standard generated endpoint rules through an external mechanism.
+ * This trait marks the presence of handwritten rules, which are added to the model by a transformer,
+ * but does not specify their behavior.
+ */
+public final class RuleBasedEndpointsTrait extends AnnotationTrait {
+    public static final ShapeId ID = ShapeId.from("aws.endpoints#rulesBasedEndpoints");
+
+    public RuleBasedEndpointsTrait(ObjectNode node) {
+        super(ID, node);
+    }
+
+    public RuleBasedEndpointsTrait() {
+        this(Node.objectNode());
+    }
+
+    public static final class Provider extends AnnotationTrait.Provider<RuleBasedEndpointsTrait> {
+        public Provider() {
+            super(ID, RuleBasedEndpointsTrait::new);
+        }
+    }
+}
diff --git a/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/StandardPartitionalEndpointsTrait.java b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/StandardPartitionalEndpointsTrait.java
new file mode 100644
index 000000000..d20319f89
--- /dev/null
+++ b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/StandardPartitionalEndpointsTrait.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.rulesengine.aws.traits;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import software.amazon.smithy.model.node.Node;
+import software.amazon.smithy.model.node.ObjectNode;
+import software.amazon.smithy.model.shapes.ShapeId;
+import software.amazon.smithy.model.traits.AbstractTrait;
+import software.amazon.smithy.model.traits.AbstractTraitBuilder;
+import software.amazon.smithy.model.traits.Trait;
+import software.amazon.smithy.utils.BuilderRef;
+import software.amazon.smithy.utils.SmithyBuilder;
+import software.amazon.smithy.utils.ToSmithyBuilder;
+
+/**
+ * An endpoints modifier trait that indicates that a service is partitional
+ * and a single endpoint should resolve per partition.
+ */
+public final class StandardPartitionalEndpointsTrait extends AbstractTrait
+        implements ToSmithyBuilder<StandardPartitionalEndpointsTrait> {
+    public static final ShapeId ID = ShapeId.from("aws.endpoints#standardPartitionalEndpoints");
+    public static final String PARTITION_ENDPOINT_SPECIAL_CASES = "partitionEndpointSpecialCases";
+    public static final String ENDPOINT_PATTERN_TYPE = "endpointPatternType";
+
+    private final Map<String, List<PartitionEndpointSpecialCase>> partitionEndpointSpecialCases;
+
+    private final EndpointPatternType endpointPatternType;
+
+    public StandardPartitionalEndpointsTrait(StandardPartitionalEndpointsTrait.Builder builder) {
+        super(ID, builder.getSourceLocation());
+        partitionEndpointSpecialCases = builder.partitionEndpointSpecialCases.copy();
+        endpointPatternType = Objects.requireNonNull(builder.endpointPatternType);
+    }
+
+    /**
+     * Gets the map of partition string to a list of partition endpoint special cases defined in the trait.
+     *
+     * @return Returns a map of partition string to a list of {@link PartitionEndpointSpecialCase}
+     */
+    public Map<String, List<PartitionEndpointSpecialCase>> getPartitionEndpointSpecialCases() {
+        return partitionEndpointSpecialCases;
+    }
+
+    /**
+     * Gets the endpoint pattern type defined in the trait.
+     *
+     * @return Returns a {@link EndpointPatternType}
+     */
+    public EndpointPatternType getEndpointPatternType() {
+        return endpointPatternType;
+    }
+
+    @Override
+    protected Node createNode() {
+        ObjectNode.Builder partitionEndpointSpecialCasesNodeBuilder = ObjectNode.objectNodeBuilder();
+        for (Map.Entry<String, List<PartitionEndpointSpecialCase>> entry
+                : partitionEndpointSpecialCases.entrySet()) {
+            List<Node> nodes = new ArrayList<>();
+            for (PartitionEndpointSpecialCase partitionEndpointSpecialCase : entry.getValue()) {
+                nodes.add(partitionEndpointSpecialCase.toNode());
+            }
+            partitionEndpointSpecialCasesNodeBuilder.withMember(entry.getKey(), Node.fromNodes(nodes));
+        }
+
+        return ObjectNode.objectNodeBuilder()
+                .sourceLocation(getSourceLocation())
+                .withMember(PARTITION_ENDPOINT_SPECIAL_CASES, partitionEndpointSpecialCasesNodeBuilder.build())
+                .withMember(ENDPOINT_PATTERN_TYPE, endpointPatternType.getName())
+                .build();
+    }
+
+    @Override
+    public SmithyBuilder<StandardPartitionalEndpointsTrait> toBuilder() {
+        return new Builder()
+                .partitionEndpointSpecialCases(partitionEndpointSpecialCases)
+                .endpointPatternType(endpointPatternType);
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static final class Provider extends AbstractTrait.Provider {
+        public Provider() {
+            super(ID);
+        }
+
+        @Override
+        public Trait createTrait(ShapeId target, Node value) {
+            ObjectNode objectNode = value.expectObjectNode();
+
+            EndpointPatternType endpointPatternType = EndpointPatternType
+                .fromNode(objectNode.expectStringMember(ENDPOINT_PATTERN_TYPE));
+
+            StandardPartitionalEndpointsTrait.Builder builder = builder()
+                    .sourceLocation(value)
+                    .endpointPatternType(endpointPatternType);
+
+            if (objectNode.containsMember(PARTITION_ENDPOINT_SPECIAL_CASES)) {
+                for (Map.Entry<String, Node> entry
+                        : objectNode.expectObjectMember(PARTITION_ENDPOINT_SPECIAL_CASES).getStringMap().entrySet()) {
+                    List<PartitionEndpointSpecialCase> partitionEndpointSpecialCases = new ArrayList<>();
+                    for (Node node: entry.getValue().expectArrayNode().getElements()) {
+                        partitionEndpointSpecialCases.add(PartitionEndpointSpecialCase.fromNode(node));
+                    }
+                    builder.putPartitionEndpointSpecialCase(
+                        entry.getKey(), Collections.unmodifiableList(partitionEndpointSpecialCases));
+                }
+            }
+
+            StandardPartitionalEndpointsTrait result = builder.build();
+            result.setNodeCache(value);
+            return result;
+        }
+    }
+
+    public static final class Builder extends AbstractTraitBuilder<StandardPartitionalEndpointsTrait, Builder> {
+        private final BuilderRef<Map<String, List<PartitionEndpointSpecialCase>>> partitionEndpointSpecialCases =
+            BuilderRef.forOrderedMap();
+        private EndpointPatternType endpointPatternType;
+
+        /**
+         * Sets the partition endpoint special cases.
+         *
+         * @param partitionEndpointSpecialCases Map of partition string to list of {@link PartitionEndpointSpecialCase}
+         * @return Returns the builder.
+         */
+        public Builder partitionEndpointSpecialCases(
+            Map<String, List<PartitionEndpointSpecialCase>> partitionEndpointSpecialCases
+        ) {
+            this.partitionEndpointSpecialCases.clear();
+            this.partitionEndpointSpecialCases.get().putAll(partitionEndpointSpecialCases);
+            return this;
+        }
+
+        /**
+         * Sets the list of partition endpoint special cases for a partition.
+         *
+         * @param partition the partition to use
+         * @param partitionEndpointSpecialCases Map of partition string to list of {@link PartitionEndpointSpecialCase}
+         * @return Returns the builder.
+         */
+        public Builder putPartitionEndpointSpecialCase(
+            String partition,
+            List<PartitionEndpointSpecialCase> partitionEndpointSpecialCases
+        ) {
+            this.partitionEndpointSpecialCases.get().put(partition, partitionEndpointSpecialCases);
+            return this;
+        }
+
+        /**
+         * Sets the endpoint pattern type.
+         *
+         * @param endpointPatternType the endpoint pattern type to use
+         * @return Returns the builder.
+         */
+        public Builder endpointPatternType(EndpointPatternType endpointPatternType) {
+            this.endpointPatternType = endpointPatternType;
+            return this;
+        }
+
+        @Override
+        public StandardPartitionalEndpointsTrait build() {
+            return new StandardPartitionalEndpointsTrait(this);
+        }
+    }
+}
diff --git a/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/StandardRegionalEndpointsTrait.java b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/StandardRegionalEndpointsTrait.java
new file mode 100644
index 000000000..0b867846b
--- /dev/null
+++ b/smithy-aws-endpoints/src/main/java/software/amazon/smithy/rulesengine/aws/traits/StandardRegionalEndpointsTrait.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.rulesengine.aws.traits;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import software.amazon.smithy.model.node.Node;
+import software.amazon.smithy.model.node.ObjectNode;
+import software.amazon.smithy.model.shapes.ShapeId;
+import software.amazon.smithy.model.traits.AbstractTrait;
+import software.amazon.smithy.model.traits.AbstractTraitBuilder;
+import software.amazon.smithy.model.traits.Trait;
+import software.amazon.smithy.utils.BuilderRef;
+import software.amazon.smithy.utils.SmithyBuilder;
+import software.amazon.smithy.utils.ToSmithyBuilder;
+
+/**
+ * An endpoints modifier trait that indicates that a service's endpoints should be resolved
+ * using the standard AWS regional patterns.
+ */
+public final class StandardRegionalEndpointsTrait extends AbstractTrait
+        implements ToSmithyBuilder<StandardRegionalEndpointsTrait> {
+    public static final ShapeId ID = ShapeId.from("aws.endpoints#standardRegionalEndpoints");
+    public static final String PARTITION_SPECIAL_CASES = "partitionSpecialCases";
+    public static final String REGION_SPECIAL_CASES = "regionSpecialCases";
+
+    private final Map<String, List<PartitionSpecialCase>> partitionSpecialCases;
+    private final Map<String, List<RegionSpecialCase>> regionSpecialCases;
+
+    public StandardRegionalEndpointsTrait(StandardRegionalEndpointsTrait.Builder builder) {
+        super(ID, builder.getSourceLocation());
+        partitionSpecialCases = builder.partitionSpecialCases.copy();
+        regionSpecialCases = builder.regionSpecialCases.copy();
+    }
+
+    /**
+     * Gets the map of partition string to a list of partition special cases defined in the trait.
+     *
+     * @return Returns a map of partition string to a list of {@link PartitionSpecialCase}
+     */
+    public Map<String, List<PartitionSpecialCase>> getPartitionSpecialCases() {
+        return partitionSpecialCases;
+    }
+
+    /**
+     * Gets the map of partition string to a list of region special cases defined in the trait.
+     *
+     * @return Returns a map of region string to a list of {@link RegionSpecialCase}
+     */
+    public Map<String, List<RegionSpecialCase>> getRegionSpecialCases() {
+        return regionSpecialCases;
+    }
+
+    @Override
+    protected Node createNode() {
+        ObjectNode.Builder partitionSpecialCasesNodeBuilder = ObjectNode.objectNodeBuilder();
+        for (Map.Entry<String, List<PartitionSpecialCase>> entry: partitionSpecialCases.entrySet()) {
+            List<Node> nodes = new ArrayList<>();
+            for (PartitionSpecialCase partitionSpecialCase : entry.getValue()) {
+                nodes.add(partitionSpecialCase.toNode());
+            }
+            partitionSpecialCasesNodeBuilder.withMember(entry.getKey(), Node.fromNodes(nodes));
+        }
+
+        ObjectNode.Builder regionSpecialCasesNodeBuilder = ObjectNode.objectNodeBuilder();
+        for (Map.Entry<String, List<RegionSpecialCase>> entry: regionSpecialCases.entrySet()) {
+            List<Node> nodes = new ArrayList<>();
+            for (RegionSpecialCase regionSpecialCase : entry.getValue()) {
+                nodes.add(regionSpecialCase.toNode());
+            }
+            regionSpecialCasesNodeBuilder.withMember(entry.getKey(), Node.fromNodes(nodes));
+        }
+
+        return ObjectNode.objectNodeBuilder()
+                .sourceLocation(getSourceLocation())
+                .withMember(PARTITION_SPECIAL_CASES, partitionSpecialCasesNodeBuilder.build())
+                .withMember(REGION_SPECIAL_CASES, partitionSpecialCasesNodeBuilder.build())
+                .build();
+    }
+
+    @Override
+    public SmithyBuilder<StandardRegionalEndpointsTrait> toBuilder() {
+        return new Builder()
+                .partitionSpecialCases(partitionSpecialCases)
+                .regionSpecialCases(regionSpecialCases);
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static final class Provider extends AbstractTrait.Provider {
+        public Provider() {
+            super(ID);
+        }
+
+        @Override
+        public Trait createTrait(ShapeId target, Node value) {
+            ObjectNode objectNode = value.expectObjectNode();
+
+            StandardRegionalEndpointsTrait.Builder builder = builder()
+                    .sourceLocation(value);
+
+            if (objectNode.containsMember(PARTITION_SPECIAL_CASES)) {
+                for (Map.Entry<String, Node> entry
+                        : objectNode.expectObjectMember(PARTITION_SPECIAL_CASES).getStringMap().entrySet()) {
+                    List<PartitionSpecialCase> partitionSpecialCases = new ArrayList<>();
+                    for (Node node: entry.getValue().expectArrayNode().getElements()) {
+                        partitionSpecialCases.add(PartitionSpecialCase.fromNode(node));
+                    }
+                    builder.putPartitionSpecialCases(
+                        entry.getKey(), Collections.unmodifiableList(partitionSpecialCases));
+                }
+            }
+
+            if (objectNode.containsMember(REGION_SPECIAL_CASES)) {
+                for (Map.Entry<String, Node> entry
+                        : objectNode.expectObjectMember(REGION_SPECIAL_CASES).getStringMap().entrySet()) {
+                    List<RegionSpecialCase> regionSpecialCases = new ArrayList<>();
+                    for (Node node: entry.getValue().expectArrayNode().getElements()) {
+                        regionSpecialCases.add(RegionSpecialCase.fromNode(node));
+                    }
+                    builder.putRegionSpecialCases(entry.getKey(), Collections.unmodifiableList(regionSpecialCases));
+                }
+            }
+
+            StandardRegionalEndpointsTrait result = builder.build();
+            result.setNodeCache(value);
+            return result;
+        }
+    }
+
+    public static final class Builder extends AbstractTraitBuilder<StandardRegionalEndpointsTrait, Builder> {
+        private final BuilderRef<Map<String, List<PartitionSpecialCase>>> partitionSpecialCases =
+                BuilderRef.forOrderedMap();
+        private final BuilderRef<Map<String, List<RegionSpecialCase>>> regionSpecialCases =
+                BuilderRef.forOrderedMap();
+
+        /**
+         * Sets the partition special cases.
+         *
+         * @param partitionSpecialCases Map of partition string to list of {@link PartitionSpecialCase}
+         * @return Returns the builder.
+         */
+        public Builder partitionSpecialCases(Map<String, List<PartitionSpecialCase>> partitionSpecialCases) {
+            this.partitionSpecialCases.clear();
+            this.partitionSpecialCases.get().putAll(partitionSpecialCases);
+            return this;
+        }
+
+        /**
+         * Sets the list of partition special cases for a partition.
+         *
+         * @param partition the partition to use
+         * @param partitionSpecialCases Map of partition string to list of {@link PartitionSpecialCase}
+         * @return Returns the builder.
+         */
+        public Builder putPartitionSpecialCases(String partition, List<PartitionSpecialCase> partitionSpecialCases) {
+            this.partitionSpecialCases.get().put(partition, partitionSpecialCases);
+            return this;
+        }
+
+        /**
+         * Sets the region special cases.
+         *
+         * @param regionSpecialCases Map of region string to list of {@link RegionSpecialCase}
+         * @return Returns the builder.
+         */
+        public Builder regionSpecialCases(Map<String, List<RegionSpecialCase>> regionSpecialCases) {
+            this.regionSpecialCases.clear();
+            this.regionSpecialCases.get().putAll(regionSpecialCases);
+            return this;
+        }
+
+        /**
+         * Sets the list of region special cases for a region.
+         *
+         * @param region the region to use
+         * @param regionSpecialCases Map of region string to list of {@link RegionSpecialCase}
+         * @return Returns the builder.
+         */
+        public Builder putRegionSpecialCases(String region, List<RegionSpecialCase> regionSpecialCases) {
+            this.regionSpecialCases.get().put(region, regionSpecialCases);
+            return this;
+        }
+
+        @Override
+        public StandardRegionalEndpointsTrait build() {
+            return new StandardRegionalEndpointsTrait(this);
+        }
+    }
+}
diff --git a/smithy-aws-endpoints/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService b/smithy-aws-endpoints/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService
new file mode 100644
index 000000000..f7f394216
--- /dev/null
+++ b/smithy-aws-endpoints/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService
@@ -0,0 +1,5 @@
+software.amazon.smithy.rulesengine.aws.traits.EndpointModifierTrait$Provider
+software.amazon.smithy.rulesengine.aws.traits.DualStackOnlyEndpointsTrait$Provider
+software.amazon.smithy.rulesengine.aws.traits.RuleBasedEndpointsTrait$Provider
+software.amazon.smithy.rulesengine.aws.traits.StandardRegionalEndpointsTrait$Provider
+software.amazon.smithy.rulesengine.aws.traits.StandardPartitionalEndpointsTrait$Provider
diff --git a/smithy-aws-endpoints/src/main/resources/META-INF/smithy/aws.endpoints.smithy b/smithy-aws-endpoints/src/main/resources/META-INF/smithy/aws.endpoints.smithy
new file mode 100644
index 000000000..cfcaf96bd
--- /dev/null
+++ b/smithy-aws-endpoints/src/main/resources/META-INF/smithy/aws.endpoints.smithy
@@ -0,0 +1,132 @@
+$version: "2.0"
+
+namespace aws.endpoints
+
+/// Marks a trait as an endpoints modifier defining trait.
+///
+/// The targeted trait must only be applied to service shapes,
+/// must be a structure, and must have the `trait` trait.
+@trait(
+    selector: "structure[trait|trait]",
+    breakingChanges: [{change: "presence"}]
+)
+structure endpointsModifier { }
+
+/// Marks that a services endpoints should be resolved using
+/// standard regional endpoint patterns.
+@trait(
+    selector: "service",
+    conflicts: [standardPartitionalEndpoints],
+    breakingChanges: [{change: "remove"}]
+)
+@endpointsModifier
+@unstable
+structure standardRegionalEndpoints {
+    /// A list of partition special cases - endpoints for a partition that do not follow the standard patterns.
+    partitionSpecialCases: PartitionSpecialCaseMap,
+    /// A list of regional special cases - endpoints for a region that do not follow the standard patterns.
+    regionSpecialCases: RegionSpecialCaseMap
+}
+
+@private
+map PartitionSpecialCaseMap {
+    key: String,
+    value: PartitionSpecialCaseList
+}
+
+@private
+list PartitionSpecialCaseList {
+    member: PartitionSpecialCase
+}
+
+@private
+structure PartitionSpecialCase {
+    @required
+    endpoint: String,
+
+    dualStack: Boolean,
+    fips: Boolean
+}
+
+@private
+map RegionSpecialCaseMap {
+    key: String,
+    value: RegionSpecialCaseList
+}
+
+@private
+list RegionSpecialCaseList {
+    member: RegionSpecialCase
+}
+
+@private
+structure RegionSpecialCase {
+    @required
+    endpoint: String,
+
+    dualStack: Boolean,
+    fips: Boolean,
+    signingRegion: String
+}
+
+/// Marks that a services is non-regionalized and has
+/// a single endpoint in each partition.
+@trait(
+    selector: "service",
+    conflicts: [standardRegionalEndpoints],
+    breakingChanges: [{change: "any"}]
+)
+@endpointsModifier
+@unstable
+structure standardPartitionalEndpoints {
+    /// The pattern type to use for the partition endpoint.
+    @required
+    endpointPatternType: PartitionEndpointPattern,
+
+    /// A list of partition endpoint special cases - partitions that do not follow the services standard patterns
+    /// or are located in a region other than the partition's defaultGlobalRegion.
+    partitionEndpointSpecialCases: PartitionEndpointSpecialCaseMap,
+}
+
+@private
+enum PartitionEndpointPattern {
+    SERVICE_DNSSUFFIX = "service_dnsSuffix"
+    SERVICE_REGION_DNSSUFFIX = "service_region_dnsSuffix"
+}
+
+@private
+map PartitionEndpointSpecialCaseMap {
+    key: String,
+    value: PartitionEndpointSpecialCaseList
+}
+
+@private
+list PartitionEndpointSpecialCaseList {
+    member: PartitionEndpointSpecialCase
+}
+
+@private
+structure PartitionEndpointSpecialCase {
+    endpoint: String,
+    region: String,
+    dualStack: Boolean,
+    fips: Boolean,
+}
+
+/// Marks that a services has only dualStack endpoints.
+@trait(
+    selector: "service",
+    breakingChanges: [{change: "any"}]
+)
+@endpointsModifier
+@unstable
+structure dualStackOnlyEndpoints { }
+
+/// Marks that a services has hand written endpoint rules.
+@trait(
+    selector: "service",
+    breakingChanges: [{change: "any"}]
+)
+@endpointsModifier
+@unstable
+structure rulesBasedEndpoints { }
diff --git a/smithy-aws-endpoints/src/main/resources/META-INF/smithy/manifest b/smithy-aws-endpoints/src/main/resources/META-INF/smithy/manifest
new file mode 100644
index 000000000..665fc53b2
--- /dev/null
+++ b/smithy-aws-endpoints/src/main/resources/META-INF/smithy/manifest
@@ -0,0 +1,1 @@
+aws.endpoints.smithy
diff --git a/smithy-aws-endpoints/src/main/resources/software/amazon/smithy/rulesengine/aws/language/functions/partition/partitions.json b/smithy-aws-endpoints/src/main/resources/software/amazon/smithy/rulesengine/aws/language/functions/partition/partitions.json
index b5f17db83..1910fe30e 100644
--- a/smithy-aws-endpoints/src/main/resources/software/amazon/smithy/rulesengine/aws/language/functions/partition/partitions.json
+++ b/smithy-aws-endpoints/src/main/resources/software/amazon/smithy/rulesengine/aws/language/functions/partition/partitions.json
@@ -105,6 +105,32 @@
         "us-isob-east-1": {},
         "aws-iso-b-global": {}
       }
+    },
+    {
+      "id" : "aws-iso-e",
+      "outputs" : {
+        "dnsSuffix" : "cloud.adc-e.uk",
+        "dualStackDnsSuffix" : "cloud.adc-e.uk",
+        "implicitGlobalRegion" : "eu-isoe-west-1",
+        "name" : "aws-iso-e",
+        "supportsDualStack" : false,
+        "supportsFIPS" : true
+      },
+      "regionRegex" : "^eu\\-isoe\\-\\w+\\-\\d+$",
+      "regions" : { }
+    },
+    {
+      "id" : "aws-iso-f",
+      "outputs" : {
+        "dnsSuffix" : "csp.hci.ic.gov",
+        "dualStackDnsSuffix" : "csp.hci.ic.gov",
+        "implicitGlobalRegion" : "us-isof-south-1",
+        "name" : "aws-iso-f",
+        "supportsDualStack" : false,
+        "supportsFIPS" : true
+      },
+      "regionRegex" : "^us\\-isof\\-\\w+\\-\\d+$",
+      "regions" : { }
     }
   ]
 }
diff --git a/smithy-aws-traits/src/main/java/software/amazon/smithy/aws/traits/clientendpointdiscovery/ClientEndpointDiscoveryValidator.java b/smithy-aws-traits/src/main/java/software/amazon/smithy/aws/traits/clientendpointdiscovery/ClientEndpointDiscoveryValidator.java
index edd0608a5..20fe3fa0d 100644
--- a/smithy-aws-traits/src/main/java/software/amazon/smithy/aws/traits/clientendpointdiscovery/ClientEndpointDiscoveryValidator.java
+++ b/smithy-aws-traits/src/main/java/software/amazon/smithy/aws/traits/clientendpointdiscovery/ClientEndpointDiscoveryValidator.java
@@ -42,6 +42,9 @@ import software.amazon.smithy.utils.SmithyInternalApi;
 @SmithyInternalApi
 public final class ClientEndpointDiscoveryValidator extends AbstractValidator {
     private static final Set<String> VALID_INPUT_MEMBERS = SetUtils.of("Operation", "Identifiers");
+    private static final String MISSING_ERROR_DEFINITION = "MissingErrorDefinition";
+    private static final String UNBOUND_OPERATION = "UnboundOperation";
+    private static final String NO_OPERATIONS = "NoOperations";
 
     @Override
     public List<ValidationEvent> validate(Model model) {
@@ -73,7 +76,8 @@ public final class ClientEndpointDiscoveryValidator extends AbstractValidator {
     private List<ValidationEvent> validateTrait(ServiceShape service, ClientEndpointDiscoveryTrait trait) {
         if (!trait.getOptionalError().isPresent()) {
             return ListUtils.of(danger(service, trait,
-                    "Services SHOULD define an error which indicates an endpoint is invalid."));
+                    "Services SHOULD define an error which indicates an endpoint is invalid.",
+                    MISSING_ERROR_DEFINITION));
         }
         return Collections.emptyList();
     }
@@ -86,8 +90,9 @@ public final class ClientEndpointDiscoveryValidator extends AbstractValidator {
                 .filter(pair -> !pair.getKey().getAllOperations().contains(pair.getValue().getOperation()))
                 .map(pair -> error(pair.getKey(), String.format(
                         "The operation `%s` must be bound to the service `%s` to use it as the endpoint operation.",
-                        pair.getValue().getOperation(), pair.getKey()
-                )))
+                        pair.getValue().getOperation(), pair.getKey()),
+                        UNBOUND_OPERATION, pair.getValue().getOperation().getName()
+                ))
                 .collect(Collectors.toList());
 
         validationEvents.addAll(endpointDiscoveryServices.keySet().stream()
@@ -96,8 +101,9 @@ public final class ClientEndpointDiscoveryValidator extends AbstractValidator {
                         "The service `%s` is configured to use endpoint discovery, but has no operations bound with "
                                 + "the `%s` trait.",
                         service.getId().toString(),
-                        ClientDiscoveredEndpointTrait.ID.toString()
-                )))
+                        ClientDiscoveredEndpointTrait.ID.toString()),
+                        NO_OPERATIONS
+                ))
                 .collect(Collectors.toList()));
         return validationEvents;
     }
diff --git a/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/SmithyIntegration.java b/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/SmithyIntegration.java
index 5ab5ddad2..a0a5ff9bf 100644
--- a/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/SmithyIntegration.java
+++ b/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/SmithyIntegration.java
@@ -19,6 +19,7 @@ import java.util.Collections;
 import java.util.List;
 import software.amazon.smithy.build.FileManifest;
 import software.amazon.smithy.model.Model;
+import software.amazon.smithy.model.node.ObjectNode;
 import software.amazon.smithy.utils.AbstractCodeWriter;
 import software.amazon.smithy.utils.CodeInterceptor;
 import software.amazon.smithy.utils.CodeSection;
@@ -70,6 +71,49 @@ public interface SmithyIntegration<S, W extends SymbolWriter<W, ?>, C extends Co
         return 0;
     }
 
+    /**
+     * Configures the integration.
+     *
+     * <p>This provides access to both the parsed settings for the generator and
+     * an unparsed {@link ObjectNode} containing settings for this particular
+     * integration.
+     *
+     * <p>The following {@code smithy-build.json} file contains an example of how
+     * this configuration will be set.
+     *
+     * <pre>{@code
+     * {
+     *     "version": "1.0",
+     *     "projections": {
+     *         "codegen-projection": {
+     *             "plugins": {
+     *                 "code-generator": {
+     *                     "service": "com.example#DocumentedService",
+     *                     "integrations": {
+     *                         "my-integration": {
+     *                             "example-setting": "foo"
+     *                         }
+     *                     }
+     *                 }
+     *             }
+     *         }
+     *     }
+     * }
+     * }</pre>
+     *
+     * <p>In this example, an integration whose {@link #name} is {@code my-integration}
+     * Would receive the extra settings from the key of the same name within the
+     * {@code integrations} node.
+     *
+     * <p>Integrations SHOULD use modeled traits as much as possible to drive
+     * configuration. This is intended for configuration that doesn't make sense
+     * as a trait, such as configuring a documentation theme.
+     *
+     * @param settings Settings used to generate code.
+     * @param integrationSettings Settings used to configure integrations.
+     */
+    default void configure(S settings, ObjectNode integrationSettings) {}
+
     /**
      * Gets the names of integrations that this integration must come before.
      *
@@ -103,7 +147,7 @@ public interface SmithyIntegration<S, W extends SymbolWriter<W, ?>, C extends Co
      * <p>By default, this method will return the given {@code model} as-is.
      *
      * @param model Model being generated.
-     * @param settings Setting used to generate code.
+     * @param settings Settings used to generate code.
      * @return Returns the updated model.
      */
     default Model preprocessModel(Model model, S settings) {
@@ -122,7 +166,7 @@ public interface SmithyIntegration<S, W extends SymbolWriter<W, ?>, C extends Co
      * <p>This integration method should be called only after {@link #preprocessModel}.
      *
      * @param model Model being generated.
-     * @param settings Setting used to generate.
+     * @param settings Settings used to generate.
      * @param symbolProvider The original {@code SymbolProvider}.
      * @return The decorated {@code SymbolProvider}.
      */
diff --git a/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/CodegenDirector.java b/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/CodegenDirector.java
index 127f746f9..81e024138 100644
--- a/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/CodegenDirector.java
+++ b/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/CodegenDirector.java
@@ -36,6 +36,7 @@ import software.amazon.smithy.model.Model;
 import software.amazon.smithy.model.neighbor.Walker;
 import software.amazon.smithy.model.node.Node;
 import software.amazon.smithy.model.node.NodeMapper;
+import software.amazon.smithy.model.node.ObjectNode;
 import software.amazon.smithy.model.shapes.EnumShape;
 import software.amazon.smithy.model.shapes.IntEnumShape;
 import software.amazon.smithy.model.shapes.OperationShape;
@@ -74,6 +75,7 @@ public final class CodegenDirector<
     private ShapeId service;
     private Model model;
     private S settings;
+    private ObjectNode integrationSettings = Node.objectNode();
     private FileManifest fileManifest;
     private Supplier<Iterable<I>> integrationFinder;
     private DirectedCodegen<C, S, I> directedCodegen;
@@ -143,6 +145,8 @@ public final class CodegenDirector<
     /**
      * Sets the required settings object used for code generation.
      *
+     * <p>{@link #integrationSettings} MUST also be set.
+     *
      * @param settings Settings object.
      */
     public void settings(S settings) {
@@ -159,6 +163,9 @@ public final class CodegenDirector<
      * You will need to manually deserialize your settings if using types that
      * are not supported by Smithy's {@link NodeMapper}.
      *
+     * <p>This will also set {@link #integrationSettings} if the {@code integrations}
+     * key is present.
+     *
      * @param settingsType Settings type to deserialize into.
      * @param settingsNode Settings node value to deserialize.
      * @return Returns the deserialized settings as this is needed to provide a service shape ID.
@@ -167,9 +174,48 @@ public final class CodegenDirector<
         LOGGER.fine(() -> "Loading codegen settings from node value: " + settingsNode.getSourceLocation());
         S deserialized = new NodeMapper().deserialize(settingsNode, settingsType);
         settings(deserialized);
+        settingsNode.asObjectNode()
+            .flatMap(node -> node.getObjectMember("integrations"))
+            .ifPresent(this::integrationSettings);
         return deserialized;
     }
 
+    /**
+     * Sets the settings node to be passed to integrations.
+     *
+     * <p>Generators MUST set this with the {@code integrations} key from their
+     * plugin settings.
+     *
+     * <pre>{@code
+     * {
+     *     "version": "1.0",
+     *     "projections": {
+     *         "codegen-projection": {
+     *             "plugins": {
+     *                 "code-generator": {
+     *                     "service": "com.example#DocumentedService",
+     *                     "integrations": {
+     *                         "my-integration": {
+     *                             "example-setting": "foo"
+     *                         }
+     *                     }
+     *                 }
+     *             }
+     *         }
+     *     }
+     * }
+     * }</pre>
+     *
+     * <p>In this example, the value of the {@code integrations} key is what must
+     * be passed to this method. The value of the {@code my-integration} key will
+     * then be provided to an integration with the name {@code my-integration}.
+     *
+     * @param integrationSettings Settings used to configure integrations.
+     */
+    public void integrationSettings(ObjectNode integrationSettings) {
+        this.integrationSettings = Objects.requireNonNull(integrationSettings);
+    }
+
     /**
      * Sets the required file manifest used to write files to disk.
      *
@@ -366,7 +412,10 @@ public final class CodegenDirector<
     private List<I> findIntegrations() {
         LOGGER.fine(() -> "Finding integration implementations of " + integrationClass.getName());
         List<I> integrations = SmithyIntegration.sort(integrationFinder.get());
-        integrations.forEach(i -> LOGGER.finest(() -> "Found integration " + i.getClass().getCanonicalName()));
+        integrations.forEach(i -> {
+            LOGGER.finest(() -> "Found integration " + i.getClass().getCanonicalName());
+            i.configure(settings, integrationSettings.getObjectMember(i.name()).orElse(Node.objectNode()));
+        });
         return integrations;
     }
 
diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/traits/ExamplesTrait.java b/smithy-model/src/main/java/software/amazon/smithy/model/traits/ExamplesTrait.java
index f3ac1a863..5dbac8017 100644
--- a/smithy-model/src/main/java/software/amazon/smithy/model/traits/ExamplesTrait.java
+++ b/smithy-model/src/main/java/software/amazon/smithy/model/traits/ExamplesTrait.java
@@ -193,6 +193,25 @@ public final class ExamplesTrait extends AbstractTrait implements ToSmithyBuilde
             return builder.build();
         }
 
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            Example example = (Example) o;
+            return allowConstraintErrors == example.allowConstraintErrors && Objects.equals(title, example.title)
+                    && Objects.equals(documentation, example.documentation) && Objects.equals(input, example.input)
+                    && Objects.equals(output, example.output) && Objects.equals(error, example.error);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(title, documentation, input, output, error, allowConstraintErrors);
+        }
+
         @Override
         public Builder toBuilder() {
             return new Builder().documentation(documentation).title(title).input(input).output(output).error(error)
diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/validation/validators/PrivateAccessValidator.java b/smithy-model/src/main/java/software/amazon/smithy/model/validation/validators/PrivateAccessValidator.java
index 3ca96db18..b99bdd686 100644
--- a/smithy-model/src/main/java/software/amazon/smithy/model/validation/validators/PrivateAccessValidator.java
+++ b/smithy-model/src/main/java/software/amazon/smithy/model/validation/validators/PrivateAccessValidator.java
@@ -24,6 +24,7 @@ import software.amazon.smithy.model.neighbor.NeighborProvider;
 import software.amazon.smithy.model.neighbor.Relationship;
 import software.amazon.smithy.model.neighbor.RelationshipType;
 import software.amazon.smithy.model.shapes.Shape;
+import software.amazon.smithy.model.shapes.ShapeId;
 import software.amazon.smithy.model.traits.PrivateTrait;
 import software.amazon.smithy.model.validation.AbstractValidator;
 import software.amazon.smithy.model.validation.ValidationEvent;
@@ -50,17 +51,18 @@ public final class PrivateAccessValidator extends AbstractValidator {
         String namespace = shape.getId().getNamespace();
         for (Relationship rel : relationships) {
             if (!rel.getShape().getId().getNamespace().equals(namespace)) {
-                ValidationEvent privateAccessValidationEvent = getPrivateAccessValidationEvent(rel);
-                events.add(privateAccessValidationEvent);
+                events.add(getPrivateAccessValidationEvent(rel));
             }
         }
     }
 
     private ValidationEvent getPrivateAccessValidationEvent(Relationship relationship) {
+        ShapeId neighborId = relationship.expectNeighborShape().getId();
         String message = String.format(
                 "This shape has an invalid %s relationship that targets a private shape, `%s`, in another namespace.",
                 relationship.getRelationshipType().toString().toLowerCase(Locale.US),
-                relationship.getNeighborShape().get().getId());
+                neighborId
+        );
 
         // For now, emit a warning for trait relationships instead of an error. This is because private access on trait
         // relationships was not being validated in the past, so emitting a warning maintains backward compatibility.


Test patch:
diff --git a/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/traits/DualStackOnlyEndpointsTraitTest.java b/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/traits/DualStackOnlyEndpointsTraitTest.java
new file mode 100644
index 000000000..0af54cc33
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/traits/DualStackOnlyEndpointsTraitTest.java
@@ -0,0 +1,27 @@
+package software.amazon.smithy.rulesengine.aws.traits;
+
+import org.junit.jupiter.api.Test;
+import software.amazon.smithy.model.Model;
+import software.amazon.smithy.model.shapes.ShapeId;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class DualStackOnlyEndpointsTraitTest {
+    @Test
+    public void loadsFromModel() {
+        final Model model = Model.assembler()
+                .discoverModels(getClass().getClassLoader())
+                .addImport(getClass().getResource("dualStackOnlyEndpoints.smithy"))
+                .assemble()
+                .unwrap();
+
+        Optional<DualStackOnlyEndpointsTrait>  trait = model
+                .expectShape(ShapeId.from("ns.foo#Service1"))
+                .asServiceShape().get()
+                .getTrait(DualStackOnlyEndpointsTrait.class);
+
+        assertTrue(trait.isPresent());
+    }
+}
diff --git a/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/traits/EndpointModifierIndexTest.java b/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/traits/EndpointModifierIndexTest.java
new file mode 100644
index 000000000..53e79c0c1
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/traits/EndpointModifierIndexTest.java
@@ -0,0 +1,51 @@
+package software.amazon.smithy.rulesengine.aws.traits;
+
+import org.junit.jupiter.api.Test;
+import software.amazon.smithy.model.Model;
+import software.amazon.smithy.model.shapes.ServiceShape;
+import software.amazon.smithy.model.shapes.ShapeId;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class EndpointModifierIndexTest {
+    @Test
+    public void loadsFromModel() {
+        final Model model = Model.assembler()
+                .discoverModels(getClass().getClassLoader())
+                .addImport(getClass().getResource("endpointModifierIndex.smithy"))
+                .assemble()
+                .unwrap();
+
+        ShapeId service1 = getServiceShapeId(model, "ns.foo#Service1");
+        ShapeId service2 = getServiceShapeId(model, "ns.foo#Service2");
+        ShapeId service3 = getServiceShapeId(model, "ns.foo#Service3");
+        ShapeId service4 = getServiceShapeId(model, "ns.foo#Service4");
+
+        EndpointModifierIndex index = new EndpointModifierIndex(model);
+
+        assertEquals(index.getEndpointModifierTraits(service1).size(), 1);
+        assertInstanceOf(StandardRegionalEndpointsTrait.class,
+                index.getEndpointModifierTraits(service1).get(StandardRegionalEndpointsTrait.ID));
+
+        assertEquals(index.getEndpointModifierTraits(service2).size(), 1);
+        assertInstanceOf(StandardPartitionalEndpointsTrait.class,
+            index.getEndpointModifierTraits(service2).get(StandardPartitionalEndpointsTrait.ID));
+
+        assertEquals(index.getEndpointModifierTraits(service3).size(), 2);
+        assertInstanceOf(StandardRegionalEndpointsTrait.class,
+            index.getEndpointModifierTraits(service3).get(StandardRegionalEndpointsTrait.ID));
+        assertInstanceOf(DualStackOnlyEndpointsTrait.class,
+            index.getEndpointModifierTraits(service3).get(DualStackOnlyEndpointsTrait.ID));
+
+        assertEquals(index.getEndpointModifierTraits(service4).size(), 2);
+        assertInstanceOf(StandardPartitionalEndpointsTrait.class,
+            index.getEndpointModifierTraits(service4).get(StandardPartitionalEndpointsTrait.ID));
+        assertInstanceOf(RuleBasedEndpointsTrait.class,
+            index.getEndpointModifierTraits(service4).get(RuleBasedEndpointsTrait.ID));
+    }
+
+    private ShapeId getServiceShapeId(Model model, String service) {
+        return model
+            .expectShape(ShapeId.from(service), ServiceShape.class).toShapeId();
+    }
+}
diff --git a/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/traits/RuleBasedEndpointsTraitTest.java b/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/traits/RuleBasedEndpointsTraitTest.java
new file mode 100644
index 000000000..91d9aa4bc
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/traits/RuleBasedEndpointsTraitTest.java
@@ -0,0 +1,27 @@
+package software.amazon.smithy.rulesengine.aws.traits;
+
+import org.junit.jupiter.api.Test;
+import software.amazon.smithy.model.Model;
+import software.amazon.smithy.model.shapes.ShapeId;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class RuleBasedEndpointsTraitTest {
+    @Test
+    public void loadsFromModel() {
+        final Model model = Model.assembler()
+                .discoverModels(getClass().getClassLoader())
+                .addImport(getClass().getResource("ruleBasedEndpoints.smithy"))
+                .assemble()
+                .unwrap();
+
+        Optional<RuleBasedEndpointsTrait>  trait = model
+                .expectShape(ShapeId.from("ns.foo#Service1"))
+                .asServiceShape().get()
+                .getTrait(RuleBasedEndpointsTrait.class);
+
+        assertTrue(trait.isPresent());
+    }
+}
diff --git a/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/traits/StandardPartitionalEndpointsTraitTest.java b/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/traits/StandardPartitionalEndpointsTraitTest.java
new file mode 100644
index 000000000..48108e249
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/traits/StandardPartitionalEndpointsTraitTest.java
@@ -0,0 +1,54 @@
+package software.amazon.smithy.rulesengine.aws.traits;
+
+import org.junit.jupiter.api.Test;
+import software.amazon.smithy.model.Model;
+import software.amazon.smithy.model.shapes.ShapeId;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+class StandardPartitionalEndpointsTraitTest {
+    @Test
+    public void loadsFromModel() {
+        final Model model = Model.assembler()
+                .discoverModels(getClass().getClassLoader())
+                .addImport(getClass().getResource("standardPartitionalEndpoints.smithy"))
+                .assemble()
+                .unwrap();
+
+        StandardPartitionalEndpointsTrait trait;
+
+        trait = getTraitFromService(model, "ns.foo#Service1");
+
+        assertEquals(trait.getEndpointPatternType(), EndpointPatternType.SERVICE_DNSSUFFIX);
+        assertEquals(trait.getPartitionEndpointSpecialCases().size(), 0);
+
+        trait = getTraitFromService(model, "ns.foo#Service2");
+
+        assertEquals(trait.getEndpointPatternType(), EndpointPatternType.SERVICE_REGION_DNSSUFFI);
+        assertEquals(trait.getPartitionEndpointSpecialCases().size(), 1);
+
+        List<PartitionEndpointSpecialCase> cases = trait.getPartitionEndpointSpecialCases().get("aws-us-gov");
+
+        PartitionEndpointSpecialCase case1 = cases.get(0);
+        assertEquals(case1.getEndpoint(), "myservice.{region}.{dnsSuffix}");
+        assertEquals(case1.getFips(), true);
+        assertEquals(case1.getRegion(), "us-east-1");
+        assertNull(case1.getDualStack());
+
+        PartitionEndpointSpecialCase case2 = cases.get(1);
+        assertEquals(case2.getEndpoint(), "myservice.global.amazonaws.com");
+        assertEquals(case2.getDualStack(), true);
+        assertEquals(case2.getRegion(), "us-west-2");
+        assertNull(case2.getFips());
+    }
+
+    private StandardPartitionalEndpointsTrait getTraitFromService(Model model, String service) {
+        return model
+            .expectShape(ShapeId.from(service))
+            .asServiceShape().get()
+            .getTrait(StandardPartitionalEndpointsTrait.class).get();
+    }
+}
diff --git a/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/traits/StandardRegionalEndpointsTraitTest.java b/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/traits/StandardRegionalEndpointsTraitTest.java
new file mode 100644
index 000000000..3fa40fc7d
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/traits/StandardRegionalEndpointsTraitTest.java
@@ -0,0 +1,57 @@
+package software.amazon.smithy.rulesengine.aws.traits;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.util.List;
+import org.junit.jupiter.api.Test;
+import software.amazon.smithy.model.Model;
+import software.amazon.smithy.model.shapes.ShapeId;
+
+class StandardRegionalEndpointsTraitTest {
+    @Test
+    public void loadsFromModel() {
+        final Model model = Model.assembler()
+                .discoverModels(getClass().getClassLoader())
+                .addImport(getClass().getResource("standardRegionalEndpoints.smithy"))
+                .assemble()
+                .unwrap();
+        StandardRegionalEndpointsTrait trait;
+
+        trait = getTraitFromService(model, "ns.foo#Service1");
+
+        assertEquals(trait.getPartitionSpecialCases().size(), 0);
+        assertEquals(trait.getRegionSpecialCases().size(), 0);
+
+        trait = getTraitFromService(model, "ns.foo#Service2");
+
+        assertEquals(trait.getRegionSpecialCases().size(), 0);
+        assertEquals(trait.getRegionSpecialCases().size(), 0);
+
+        trait = getTraitFromService(model, "ns.foo#Service3");
+
+        assertEquals(trait.getRegionSpecialCases().size(), 1);
+        assertEquals(trait.getRegionSpecialCases().size(), 1);
+        List<PartitionSpecialCase> partitionSpecialCases = trait.getPartitionSpecialCases().get("aws-us-gov");
+
+        PartitionSpecialCase partitionSpecialCase1 = partitionSpecialCases.get(0);
+        assertEquals(partitionSpecialCase1.getEndpoint(), "myservice.{region}.{dnsSuffix}");
+        assertEquals(partitionSpecialCase1.getFips(), true);
+        assertNull(partitionSpecialCase1.getDualStack());
+
+        PartitionSpecialCase partitionSpecialCase2 = partitionSpecialCases.get(1);
+        assertEquals(partitionSpecialCase2.getEndpoint(), "myservice.global.amazonaws.com");
+        assertEquals(partitionSpecialCase2.getDualStack(), true);
+        assertNull(partitionSpecialCase2.getFips());
+
+        List<RegionSpecialCase> regionSpecialCases = trait.getRegionSpecialCases().get("us-east-1");
+        assertEquals(regionSpecialCases.size(), 0);
+    }
+
+    private StandardRegionalEndpointsTrait getTraitFromService(Model model, String service) {
+        return model
+            .expectShape(ShapeId.from(service))
+            .asServiceShape().get()
+            .getTrait(StandardRegionalEndpointsTrait.class).get();
+    }
+}
diff --git a/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/traits/TestRunnerTest.java b/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/traits/TestRunnerTest.java
new file mode 100644
index 000000000..f051cfece
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/java/software/amazon/smithy/rulesengine/aws/traits/TestRunnerTest.java
@@ -0,0 +1,21 @@
+package software.amazon.smithy.rulesengine.aws.traits;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+import software.amazon.smithy.model.validation.testrunner.SmithyTestCase;
+import software.amazon.smithy.model.validation.testrunner.SmithyTestSuite;
+
+import java.util.concurrent.Callable;
+import java.util.stream.Stream;
+
+public class TestRunnerTest {
+    public static Stream<?> source() {
+        return SmithyTestSuite.defaultParameterizedTestSource(TestRunnerTest.class);
+    }
+
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("source")
+    public void testRunner(String filename, Callable<SmithyTestCase.Result> callable) throws Exception {
+        callable.call();
+    }
+}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/dualStackOnlyEndpoints.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/dualStackOnlyEndpoints.smithy
new file mode 100644
index 000000000..427f157b2
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/dualStackOnlyEndpoints.smithy
@@ -0,0 +1,10 @@
+$version: "2.0"
+
+namespace ns.foo
+
+use aws.endpoints#dualStackOnlyEndpoints
+
+@dualStackOnlyEndpoints
+service Service1 {
+    version: "2021-06-29"
+}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/endpointModifierIndex.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/endpointModifierIndex.smithy
new file mode 100644
index 000000000..8d8cc1be7
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/endpointModifierIndex.smithy
@@ -0,0 +1,63 @@
+$version: "2.0"
+
+namespace ns.foo
+
+use aws.endpoints#standardRegionalEndpoints
+use aws.endpoints#standardPartitionalEndpoints
+use aws.endpoints#dualStackOnlyEndpoints
+use aws.endpoints#rulesBasedEndpoints
+
+@standardRegionalEndpoints
+service Service1 {
+    version: "2021-06-29"
+}
+
+@standardPartitionalEndpoints(endpointPatternType: "service_dnsSuffix")
+service Service2 {
+    version: "2021-06-29"
+}
+
+@standardRegionalEndpoints(
+    partitionSpecialCases: {
+        "aws-us-gov": [
+            {
+                endpoint: "myservice.{region}.{dnsSuffix}",
+                dualStack: true
+            },
+            {
+                endpoint: "myservice.global.amazonaws.com",
+                dualStack: true
+            }
+        ]
+    },
+    regionSpecialCases: {
+        "us-east-1": [
+        ]
+    }
+)
+@dualStackOnlyEndpoints
+service Service3 {
+    version: "2021-06-29"
+}
+
+@standardPartitionalEndpoints(
+    endpointPatternType: "service_region_dnsSuffix",
+    partitionEndpointSpecialCases: {
+        "aws-us-gov": [
+            {
+                endpoint: "myservice.{region}.{dnsSuffix}",
+                region: "us-east-1"
+                fips: true
+            },
+            {
+                endpoint: "myservice.global.amazonaws.com",
+                region: "us-west-2"
+                dualStack: true
+            }
+        ]
+    }
+)
+@rulesBasedEndpoints
+service Service4 {
+    version: "2021-06-29"
+}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/dualstack-only-endpoints-trait.errors b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/dualstack-only-endpoints-trait.errors
new file mode 100644
index 000000000..aeba2d7eb
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/dualstack-only-endpoints-trait.errors
@@ -0,0 +1,1 @@
+[WARNING] smithy.example#MyService: This shape applies a trait that is unstable: aws.endpoints#dualStackOnlyEndpoints | UnstableTrait.aws.endpoints#dualStackOnlyEndpoints
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/dualstack-only-endpoints-trait.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/dualstack-only-endpoints-trait.smithy
new file mode 100644
index 000000000..586dcd340
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/dualstack-only-endpoints-trait.smithy
@@ -0,0 +1,10 @@
+$version: "2"
+
+namespace smithy.example
+
+use aws.endpoints#dualStackOnlyEndpoints
+
+@dualStackOnlyEndpoints
+service MyService {
+    version: "2020-04-02"
+}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/rule-based-endpoints-trait.errors b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/rule-based-endpoints-trait.errors
new file mode 100644
index 000000000..780a64af8
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/rule-based-endpoints-trait.errors
@@ -0,0 +1,1 @@
+[WARNING] smithy.example#MyService: This shape applies a trait that is unstable: aws.endpoints#rulesBasedEndpoints | UnstableTrait.aws.endpoints#rulesBasedEndpoints
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/rule-based-endpoints-trait.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/rule-based-endpoints-trait.smithy
new file mode 100644
index 000000000..cf17ea284
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/rule-based-endpoints-trait.smithy
@@ -0,0 +1,10 @@
+$version: "2"
+
+namespace smithy.example
+
+use aws.endpoints#rulesBasedEndpoints
+
+@rulesBasedEndpoints
+service MyService {
+    version: "2020-04-02"
+}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/standard-partitional-endpoints-trait.errors b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/standard-partitional-endpoints-trait.errors
new file mode 100644
index 000000000..c0c29a135
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/standard-partitional-endpoints-trait.errors
@@ -0,0 +1,1 @@
+[WARNING] smithy.example#MyService: This shape applies a trait that is unstable: aws.endpoints#standardPartitionalEndpoints | UnstableTrait.aws.endpoints#standardPartitionalEndpoints
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/standard-partitional-endpoints-trait.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/standard-partitional-endpoints-trait.smithy
new file mode 100644
index 000000000..8e6bcdaed
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/standard-partitional-endpoints-trait.smithy
@@ -0,0 +1,16 @@
+$version: "2"
+
+namespace smithy.example
+
+use aws.endpoints#standardPartitionalEndpoints
+
+@standardPartitionalEndpoints(
+    endpointPatternType: "service_dnsSuffix",
+    partitionEndpointSpecialCases: {
+        "aws-us": [{endpoint: "myservice.us-west-2.amazonaws.com", region: "us-west-2"}],
+        "aws-cn": [{endpoint: "myservice.cn-north-1.amazonaws.com", region: "cn-north-1"}]
+    }
+)
+service MyService {
+    version: "2020-04-02"
+}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/standard-regional-endpoints-trait.errors b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/standard-regional-endpoints-trait.errors
new file mode 100644
index 000000000..ee89d604f
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/standard-regional-endpoints-trait.errors
@@ -0,0 +1,1 @@
+[WARNING] smithy.example#MyService: This shape applies a trait that is unstable: aws.endpoints#standardRegionalEndpoints | UnstableTrait.aws.endpoints#standardRegionalEndpoints
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/standard-regional-endpoints-trait.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/standard-regional-endpoints-trait.smithy
new file mode 100644
index 000000000..7d5bba101
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/errorfiles/standard-regional-endpoints-trait.smithy
@@ -0,0 +1,23 @@
+$version: "2"
+
+namespace smithy.example
+
+use aws.endpoints#standardRegionalEndpoints
+
+@standardRegionalEndpoints(
+    partitionSpecialCases: {
+        "aws-us-gov": [
+            {
+                endpoint: "myservice.{region}.{dnsSuffix}",
+                fips: true
+            }
+        ]
+    },
+    regionSpecialCases: {
+        "us-east-1": [
+        ]
+    }
+)
+service MyService {
+    version: "2020-04-02"
+}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/ruleBasedEndpoints.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/ruleBasedEndpoints.smithy
new file mode 100644
index 000000000..38ee8f62d
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/ruleBasedEndpoints.smithy
@@ -0,0 +1,10 @@
+$version: "2.0"
+
+namespace ns.foo
+
+use aws.endpoints#rulesBasedEndpoints
+
+@rulesBasedEndpoints
+service Service1 {
+    version: "2021-06-29"
+}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/standardPartitionalEndpoints.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/standardPartitionalEndpoints.smithy
new file mode 100644
index 000000000..2597dcec5
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/standardPartitionalEndpoints.smithy
@@ -0,0 +1,31 @@
+$version: "2.0"
+
+namespace ns.foo
+
+use aws.endpoints#standardPartitionalEndpoints
+
+@standardPartitionalEndpoints(endpointPatternType: "service_dnsSuffix")
+service Service1 {
+    version: "2021-06-29"
+}
+
+@standardPartitionalEndpoints(
+    endpointPatternType: "service_region_dnsSuffix",
+    partitionEndpointSpecialCases: {
+        "aws-us-gov": [
+            {
+                endpoint: "myservice.{region}.{dnsSuffix}",
+                region: "us-east-1"
+                fips: true
+            },
+            {
+                endpoint: "myservice.global.amazonaws.com",
+                region: "us-west-2"
+                dualStack: true
+            }
+        ]
+    }
+)
+service Service2 {
+    version: "2021-06-29"
+}
diff --git a/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/standardRegionalEndpoints.smithy b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/standardRegionalEndpoints.smithy
new file mode 100644
index 000000000..2bd3bf3f5
--- /dev/null
+++ b/smithy-aws-endpoints/src/test/resources/software/amazon/smithy/rulesengine/aws/traits/standardRegionalEndpoints.smithy
@@ -0,0 +1,37 @@
+$version: "2.0"
+
+namespace ns.foo
+
+use aws.endpoints#standardRegionalEndpoints
+
+@standardRegionalEndpoints
+service Service1 {
+    version: "2021-06-29"
+}
+
+@standardRegionalEndpoints(partitionSpecialCases: {}, regionSpecialCases: {})
+service Service2 {
+    version: "2021-06-29"
+}
+
+@standardRegionalEndpoints(
+    partitionSpecialCases: {
+        "aws-us-gov": [
+            {
+                endpoint: "myservice.{region}.{dnsSuffix}",
+                fips: true
+            },
+            {
+                endpoint: "myservice.global.amazonaws.com",
+                dualStack: true
+            }
+        ]
+    },
+    regionSpecialCases: {
+        "us-east-1": [
+        ]
+    }
+)
+service Service3 {
+    version: "2021-06-29"
+}
diff --git a/smithy-aws-protocol-tests/model/awsJson1_0/defaults.smithy b/smithy-aws-protocol-tests/model/awsJson1_0/defaults.smithy
new file mode 100644
index 000000000..fbac5fe08
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/awsJson1_0/defaults.smithy
@@ -0,0 +1,333 @@
+$version: "2.0"
+
+namespace aws.protocoltests.json10
+
+use aws.protocols#awsJson1_0
+use smithy.test#httpRequestTests
+use smithy.test#httpResponseTests
+
+apply OperationWithDefaults @httpRequestTests([
+    {
+        id: "AwsJson10ClientPopulatesDefaultValuesInInput"
+        documentation: "Client populates default values in input."
+        appliesTo: "client"
+        tags: ["defaults"]
+        protocol: awsJson1_0
+        method: "POST"
+        bodyMediaType: "application/json"
+        uri: "/"
+        headers: {"Content-Type": "application/x-amz-json-1.0"}
+        body: """
+            {
+                "defaults": {
+                    "defaultString": "hi",
+                    "defaultBoolean": true,
+                    "defaultList": [],
+                    "defaultDocumentMap": {},
+                    "defaultDocumentString": "hi",
+                    "defaultDocumentBoolean": true,
+                    "defaultDocumentList": [],
+                    "defaultTimestamp": 0,
+                    "defaultBlob": "YWJj",
+                    "defaultByte": 1,
+                    "defaultShort": 1,
+                    "defaultInteger": 10,
+                    "defaultLong": 100,
+                    "defaultFloat": 1.0,
+                    "defaultDouble": 1.0,
+                    "defaultMap": {},
+                    "defaultEnum": "FOO",
+                    "defaultIntEnum": 1
+                }
+            }"""
+        params: {
+            defaults: {}
+        }
+    }
+    {
+        id: "AwsJson10ClientSkipsTopLevelDefaultValuesInInput"
+        documentation: "Client skips top level default values in input."
+        appliesTo: "client"
+        tags: ["defaults"]
+        protocol: awsJson1_0
+        method: "POST"
+        bodyMediaType: "application/json"
+        uri: "/"
+        headers: {"Content-Type": "application/x-amz-json-1.0"}
+        body: """
+            {
+            }"""
+        params: {
+        }
+    }
+    {
+        id: "AwsJson10ClientUsesExplicitlyProvidedMemberValuesOverDefaults"
+        documentation: "Client uses explicitly provided member values over defaults"
+        appliesTo: "client"
+        tags: ["defaults"]
+        protocol: awsJson1_0
+        method: "POST"
+        bodyMediaType: "application/json"
+        uri: "/"
+        headers: {"Content-Type": "application/x-amz-json-1.0"}
+        params: {
+            defaults: {
+                defaultString: "bye"
+                defaultBoolean: true
+                defaultList: ["a"]
+                defaultDocumentMap: {name: "Jack"}
+                defaultDocumentString: "bye"
+                defaultDocumentBoolean: true
+                defaultDocumentList: ["b"]
+                defaultNullDocument: "notNull"
+                defaultTimestamp: 1
+                defaultBlob: "hi"
+                defaultByte: 2
+                defaultShort: 2
+                defaultInteger: 20
+                defaultLong: 200
+                defaultFloat: 2.0
+                defaultDouble: 2.0
+                defaultMap: {name: "Jack"}
+                defaultEnum: "BAR"
+                defaultIntEnum: 2
+            }
+        }
+        body: """
+            {
+                "defaults": {
+                    "defaultString": "bye",
+                    "defaultBoolean": true,
+                    "defaultList": ["a"],
+                    "defaultDocumentMap": {"name": "Jack"},
+                    "defaultDocumentString": "bye",
+                    "defaultDocumentBoolean": true,
+                    "defaultDocumentList": ["b"],
+                    "defaultNullDocument": "notNull",
+                    "defaultTimestamp": 1,
+                    "defaultBlob": "aGk=",
+                    "defaultByte": 2,
+                    "defaultShort": 2,
+                    "defaultInteger": 20,
+                    "defaultLong": 200,
+                    "defaultFloat": 2.0,
+                    "defaultDouble": 2.0,
+                    "defaultMap": {"name": "Jack"},
+                    "defaultEnum": "BAR",
+                    "defaultIntEnum": 2
+                }
+            }"""
+    }
+    {
+        id: "AwsJson10ServerPopulatesDefaultsWhenMissingInRequestBody"
+        documentation: "Server populates default values when missing in request body."
+        appliesTo: "server"
+        tags: ["defaults"]
+        protocol: awsJson1_0
+        method: "POST"
+        bodyMediaType: "application/json"
+        uri: "/"
+        headers: {"Content-Type": "application/x-amz-json-1.0"}
+        body: """
+            {
+            "defaults": {}
+            }"""
+        params: {
+            defaults: {
+                defaultString: "hi"
+                defaultBoolean: true
+                defaultList: []
+                defaultDocumentMap: {}
+                defaultDocumentString: "hi"
+                defaultDocumentBoolean: true
+                defaultDocumentList: []
+                defaultTimestamp: 0
+                defaultBlob: "abc"
+                defaultByte: 1
+                defaultShort: 1
+                defaultInteger: 10
+                defaultLong: 100
+                defaultFloat: 1.0
+                defaultDouble: 1.0
+                defaultMap: {}
+                defaultEnum: "FOO"
+                defaultIntEnum: 1
+            },
+            topLevelDefault: "hi"
+        }
+    }
+])
+
+apply OperationWithDefaults @httpResponseTests([
+    {
+        id: "AwsJson10ClientPopulatesDefaultsValuesWhenMissingInResponse"
+        documentation: "Client populates default values when missing in response."
+        appliesTo: "client"
+        tags: ["defaults"]
+        protocol: awsJson1_0
+        code: 200
+        bodyMediaType: "application/json"
+        headers: {"Content-Type": "application/x-amz-json-1.0"}
+        body: "{}"
+        params: {
+            defaultString: "hi"
+            defaultBoolean: true
+            defaultList: []
+            defaultDocumentMap: {}
+            defaultDocumentString: "hi"
+            defaultDocumentBoolean: true
+            defaultDocumentList: []
+            defaultTimestamp: 0
+            defaultBlob: "abc"
+            defaultByte: 1
+            defaultShort: 1
+            defaultInteger: 10
+            defaultLong: 100
+            defaultFloat: 1.0
+            defaultDouble: 1.0
+            defaultMap: {}
+            defaultEnum: "FOO"
+            defaultIntEnum: 1
+        }
+    }
+    {
+        id: "AwsJson10ClientIgnoresDefaultValuesIfMemberValuesArePresentInResponse"
+        documentation: "Client ignores default values if member values are present in the response."
+        appliesTo: "client"
+        tags: ["defaults"]
+        protocol: awsJson1_0
+        code: 200
+        bodyMediaType: "application/json"
+        headers: {"Content-Type": "application/x-amz-json-1.0"}
+        body: """
+            {
+                "defaultString": "bye",
+                "defaultBoolean": false,
+                "defaultList": ["a"],
+                "defaultDocumentMap": {"name": "Jack"},
+                "defaultDocumentString": "bye",
+                "defaultDocumentBoolean": false,
+                "defaultDocumentList": ["b"],
+                "defaultNullDocument": "notNull",
+                "defaultTimestamp": 2,
+                "defaultBlob": "aGk=",
+                "defaultByte": 2,
+                "defaultShort": 2,
+                "defaultInteger": 20,
+                "defaultLong": 200,
+                "defaultFloat": 2.0,
+                "defaultDouble": 2.0,
+                "defaultMap": {"name": "Jack"},
+                "defaultEnum": "BAR",
+                "defaultIntEnum": 2
+            }"""
+        params: {
+            defaultString: "bye"
+            defaultBoolean: false
+            defaultList: ["a"]
+            defaultDocumentMap: {name: "Jack"}
+            defaultDocumentString: "bye"
+            defaultDocumentBoolean: false
+            defaultDocumentList: ["b"]
+            defaultNullDocument: "notNull"
+            defaultTimestamp: 2
+            defaultBlob: "hi"
+            defaultByte: 2
+            defaultShort: 2
+            defaultInteger: 20
+            defaultLong: 200
+            defaultFloat: 2.0
+            defaultDouble: 2.0
+            defaultMap: {name: "Jack"}
+            defaultEnum: "BAR"
+            defaultIntEnum: 2
+        }
+    }
+    {
+        id: "AwsJson10ServerPopulatesDefaultsInResponseWhenMissingInParams"
+        documentation: "Server populates default values in response when missing in params."
+        appliesTo: "server"
+        tags: ["defaults"]
+        protocol: awsJson1_0
+        code: 200
+        bodyMediaType: "application/json"
+        headers: {"Content-Type": "application/x-amz-json-1.0"}
+        body: """
+            {
+                "defaultString": "hi",
+                "defaultBoolean": true,
+                "defaultList": [],
+                "defaultDocumentMap": {},
+                "defaultDocumentString": "hi",
+                "defaultDocumentBoolean": true,
+                "defaultDocumentList": [],
+                "defaultTimestamp": 0,
+                "defaultBlob": "YWJj",
+                "defaultByte": 1,
+                "defaultShort": 1,
+                "defaultInteger": 10,
+                "defaultLong": 100,
+                "defaultFloat": 1.0,
+                "defaultDouble": 1.0,
+                "defaultMap": {},
+                "defaultEnum": "FOO",
+                "defaultIntEnum": 1
+            }"""
+        params: {}
+    }
+])
+
+operation OperationWithDefaults {
+    input := {
+        defaults: Defaults
+
+        topLevelDefault: String = "hi" // Client should ignore default values in input shape
+    }
+
+    output := with [DefaultsMixin] {}
+}
+
+structure Defaults with [DefaultsMixin] {}
+
+@mixin
+structure DefaultsMixin {
+    defaultString: String = "hi"
+    defaultBoolean: Boolean = true
+    defaultList: TestStringList = []
+    defaultDocumentMap: Document = {}
+    defaultDocumentString: Document = "hi"
+    defaultDocumentBoolean: Document = true
+    defaultDocumentList: Document = []
+    defaultNullDocument: Document = null
+    defaultTimestamp: Timestamp = 0
+    defaultBlob: Blob = "abc"
+    defaultByte: Byte = 1
+    defaultShort: Short = 1
+    defaultInteger: Integer = 10
+    defaultLong: Long = 100
+    defaultFloat: Float = 1.0
+    defaultDouble: Double = 1.0
+    defaultMap: TestStringMap = {}
+    defaultEnum: TestEnum = "FOO"
+    defaultIntEnum: TestIntEnum = 1
+}
+
+list TestStringList {
+    member: String
+}
+
+map TestStringMap {
+    key: String
+    value: String
+}
+
+enum TestEnum {
+    FOO
+    BAR
+    BAZ
+}
+
+intEnum TestIntEnum {
+    ONE = 1
+    TWO = 2
+}
diff --git a/smithy-aws-protocol-tests/model/awsJson1_0/main.smithy b/smithy-aws-protocol-tests/model/awsJson1_0/main.smithy
index f01642df7..bc9e29180 100644
--- a/smithy-aws-protocol-tests/model/awsJson1_0/main.smithy
+++ b/smithy-aws-protocol-tests/model/awsJson1_0/main.smithy
@@ -30,6 +30,10 @@ service JsonRpc10 {
         HostWithPathOperation,
 
         // requestCompression trait tests
-        PutWithContentEncoding
+        PutWithContentEncoding,
+
+        OperationWithDefaults,
+        OperationWithRequiredMembers,
+        OperationWithNestedStructure
     ]
 }
diff --git a/smithy-aws-protocol-tests/model/awsJson1_0/nestedDefaults.smithy b/smithy-aws-protocol-tests/model/awsJson1_0/nestedDefaults.smithy
new file mode 100644
index 000000000..d31c7ba9b
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/awsJson1_0/nestedDefaults.smithy
@@ -0,0 +1,408 @@
+$version: "2.0"
+
+namespace aws.protocoltests.json10
+
+use aws.protocols#awsJson1_0
+use smithy.test#httpRequestTests
+use smithy.test#httpResponseTests
+
+apply OperationWithNestedStructure @httpRequestTests([
+    {
+        id: "AwsJson10ClientPopulatesNestedDefaultValuesWhenMissing"
+        documentation: "Client populates nested default values when missing."
+        appliesTo: "client"
+        tags: ["defaults"]
+        protocol: awsJson1_0
+        method: "POST"
+        bodyMediaType: "application/json"
+        uri: "/"
+        headers: {"Content-Type": "application/x-amz-json-1.0"}
+        body: """
+            {
+                "topLevel": {
+                    "dialog": {
+                        "language": "en",
+                        "greeting": "hi"
+                    },
+                    "dialogList": [
+                        {
+                            "greeting": "hi"
+                        },
+                        {
+                            "greeting": "hi",
+                            "farewell": {
+                                "phrase": "bye"
+                            }
+                        },
+                        {
+                            "language": "it",
+                            "greeting": "ciao",
+                            "farewell": {
+                                "phrase": "arrivederci"
+                            }
+                        }
+                    ],
+                    "dialogMap": {
+                        "emptyDialog": {
+                            "greeting": "hi"
+                        },
+                        "partialEmptyDialog": {
+                            "language": "en",
+                            "greeting": "hi",
+                            "farewell": {
+                                "phrase": "bye"
+                            }
+                        },
+                        "nonEmptyDialog": {
+                            "greeting": "konnichiwa",
+                            "farewell": {
+                                "phrase": "sayonara"
+                            }
+                        }
+                    }
+                }
+            }"""
+        params: {
+            "topLevel": {
+                "dialog": {
+                    "language": "en"
+                },
+                "dialogList": [
+                    {
+                    },
+                    {
+                        "farewell": {}
+                    },
+                    {
+                        "language": "it",
+                        "greeting": "ciao",
+                        "farewell": {
+                            "phrase": "arrivederci"
+                        }
+                    }
+                ],
+                "dialogMap": {
+                    "emptyDialog": {
+                    },
+                    "partialEmptyDialog": {
+                        "language": "en",
+                        "farewell": {}
+                    },
+                    "nonEmptyDialog": {
+                        "greeting": "konnichiwa",
+                        "farewell": {
+                            "phrase": "sayonara"
+                        }
+                    }
+                }
+            }
+        }
+    }
+    {
+        id: "AwsJson10ServerPopulatesNestedDefaultsWhenMissingInRequestBody"
+        documentation: "Server populates nested default values when missing in request body."
+        appliesTo: "server"
+        tags: ["defaults"]
+        protocol: awsJson1_0
+        method: "POST"
+        bodyMediaType: "application/json"
+        uri: "/"
+        headers: {"Content-Type": "application/x-amz-json-1.0"}
+        body: """
+            {
+                "topLevel": {
+                    "dialog": {
+                        "language": "en"
+                    },
+                    "dialogList": [
+                        {
+                        },
+                        {
+                            "farewell": {}
+                        },
+                        {
+                            "language": "it",
+                            "greeting": "ciao",
+                            "farewell": {
+                                "phrase": "arrivederci"
+                            }
+                        }
+                    ],
+                    "dialogMap": {
+                        "emptyDialog": {
+                        },
+                        "partialEmptyDialog": {
+                            "language": "en",
+                            "farewell": {}
+                        },
+                        "nonEmptyDialog": {
+                            "greeting": "konnichiwa",
+                            "farewell": {
+                                "phrase": "sayonara"
+                            }
+                        }
+                    }
+                }
+            }"""
+        params: {
+            "topLevel": {
+                "dialog": {
+                    "language": "en"
+                    "greeting": "hi",
+                }
+                "dialogList": [
+                    {
+                        "greeting": "hi",
+                    },
+                    {
+                        "greeting": "hi",
+                        "farewell": {
+                            "phrase": "bye",
+                        }
+                    },
+                    {
+                        "language": "it",
+                        "greeting": "ciao",
+                        "farewell": {
+                            "phrase": "arrivederci"
+                        }
+                    }
+                ],
+                "dialogMap": {
+                    "emptyDialog": {
+                        "greeting": "hi",
+                    },
+                    "partialEmptyDialog": {
+                        "language": "en",
+                        "greeting": "hi",
+                        "farewell": {
+                            "phrase": "bye",
+                        }
+                    },
+                    "nonEmptyDialog": {
+                        "greeting": "konnichiwa",
+                        "farewell": {
+                            "phrase": "sayonara"
+                        }
+                    }
+                }
+            }
+        }
+    }
+])
+
+apply OperationWithNestedStructure @httpResponseTests([
+    {
+        id: "AwsJson10ClientPopulatesNestedDefaultsWhenMissingInResponseBody"
+        documentation: "Client populates nested default values when missing in response body."
+        appliesTo: "client"
+        tags: ["defaults"]
+        protocol: awsJson1_0
+        code: 200
+        bodyMediaType: "application/json"
+        headers: {"Content-Type": "application/x-amz-json-1.0"}
+        body: """
+            {
+                "dialog": {
+                    "language": "en"
+                },
+                "dialogList": [
+                    {
+                    },
+                    {
+                        "farewell": {}
+                    },
+                    {
+                        "language": "it",
+                        "greeting": "ciao",
+                        "farewell": {
+                            "phrase": "arrivederci"
+                        }
+                    }
+                ],
+                "dialogMap": {
+                    "emptyDialog": {
+                    },
+                    "partialEmptyDialog": {
+                        "language": "en",
+                        "farewell": {}
+                    },
+                    "nonEmptyDialog": {
+                        "greeting": "konnichiwa",
+                        "farewell": {
+                            "phrase": "sayonara"
+                        }
+                    }
+                }
+            }"""
+        params: {
+            "dialog": {
+                "language": "en"
+                "greeting": "hi",
+            }
+            "dialogList": [
+                {
+                    "greeting": "hi",
+                },
+                {
+                    "greeting": "hi",
+                    "farewell": {
+                        "phrase": "bye",
+                    }
+                },
+                {
+                    "language": "it",
+                    "greeting": "ciao",
+                    "farewell": {
+                        "phrase": "arrivederci"
+                    }
+                }
+            ],
+            "dialogMap": {
+                "emptyDialog": {
+                    "greeting": "hi",
+                },
+                "partialEmptyDialog": {
+                    "language": "en",
+                    "greeting": "hi",
+                    "farewell": {
+                        "phrase": "bye",
+                    }
+                },
+                "nonEmptyDialog": {
+                    "greeting": "konnichiwa",
+                    "farewell": {
+                        "phrase": "sayonara"
+                    }
+                }
+            }
+        }
+    }
+    {
+        id: "AwsJson10ServerPopulatesNestedDefaultValuesWhenMissingInInResponseParams"
+        documentation: "Server populates nested default values when missing in response params."
+        appliesTo: "server"
+        tags: ["defaults"]
+        protocol: awsJson1_0
+        code: 200
+        bodyMediaType: "application/json"
+        headers: {"Content-Type": "application/x-amz-json-1.0"}
+        body: """
+            {
+                "dialog": {
+                    "language": "en",
+                    "greeting": "hi"
+                },
+                "dialogList": [
+                    {
+                        "greeting": "hi"
+                    },
+                    {
+                        "greeting": "hi",
+                        "farewell": {
+                            "phrase": "bye"
+                        }
+                    },
+                    {
+                        "language": "it",
+                        "greeting": "ciao",
+                        "farewell": {
+                            "phrase": "arrivederci"
+                        }
+                    }
+                ],
+                "dialogMap": {
+                    "emptyDialog": {
+                        "greeting": "hi"
+                    },
+                    "partialEmptyDialog": {
+                        "language": "en",
+                        "greeting": "hi",
+                        "farewell": {
+                            "phrase": "bye"
+                        }
+                    },
+                    "nonEmptyDialog": {
+                        "greeting": "konnichiwa",
+                        "farewell": {
+                            "phrase": "sayonara"
+                        }
+                    }
+                }
+            }"""
+        params: {
+            "dialog": {
+                "language": "en"
+            },
+            "dialogList": [
+                {
+                },
+                {
+                    "farewell": {}
+                },
+                {
+                    "language": "it",
+                    "greeting": "ciao",
+                    "farewell": {
+                        "phrase": "arrivederci"
+                    }
+                }
+            ],
+            "dialogMap": {
+                "emptyDialog": {
+                },
+                "partialEmptyDialog": {
+                    "language": "en",
+                    "farewell": {}
+                },
+                "nonEmptyDialog": {
+                    "greeting": "konnichiwa",
+                    "farewell": {
+                        "phrase": "sayonara"
+                    }
+                }
+            }
+        }
+    }
+])
+
+operation OperationWithNestedStructure {
+    input := {
+        @required
+        topLevel: TopLevel
+    }
+
+    output := with [NestedDefaultsMixin] {
+    }
+}
+
+structure TopLevel with [NestedDefaultsMixin] {
+
+}
+
+@mixin
+structure NestedDefaultsMixin {
+    @required
+    dialog: Dialog
+    dialogList: DialogList = []
+    dialogMap: DialogMap = {}
+}
+
+structure Dialog {
+    language: String
+    greeting: String = "hi"
+    farewell: Farewell
+}
+
+structure Farewell {
+    phrase: String = "bye"
+}
+
+list DialogList {
+    member: Dialog
+}
+
+map DialogMap {
+    key: String
+    value: Dialog
+}
diff --git a/smithy-aws-protocol-tests/model/awsJson1_0/required.smithy b/smithy-aws-protocol-tests/model/awsJson1_0/required.smithy
new file mode 100644
index 000000000..036bf45e9
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/awsJson1_0/required.smithy
@@ -0,0 +1,178 @@
+$version: "2.0"
+
+namespace aws.protocoltests.json10
+
+use aws.protocols#awsJson1_0
+use smithy.test#httpResponseTests
+
+apply OperationWithRequiredMembers @httpResponseTests([
+    {
+        id: "AwsJson10ClientErrorCorrectsWhenServerFailsToSerializeRequiredValues"
+        documentation: "Client error corrects when server fails to serialize required values."
+        appliesTo: "client"
+        tags: ["defaults", "error-correction"]
+        protocol: awsJson1_0
+        code: 200
+        bodyMediaType: "application/json"
+        headers: {"Content-Type": "application/x-amz-json-1.0"}
+        body: "{}"
+        params: {
+            requiredString: ""
+            requiredBoolean: false
+            requiredList: []
+            requiredTimestamp: 0
+            requiredBlob: ""
+            requiredByte: 0
+            requiredShort: 0
+            requiredInteger: 0
+            requiredLong: 0
+            requiredFloat: 0.0
+            requiredDouble: 0.0
+            requiredMap: {}
+        }
+    }
+])
+
+operation OperationWithRequiredMembers {
+    output := with [RequiredMembersMixin] {
+    }
+}
+
+apply OperationWithRequiredMembersWithDefaults @httpResponseTests([
+    {
+        id: "AwsJson10ClientErrorCorrectsWithDefaultValuesWhenServerFailsToSerializeRequiredValues"
+        documentation: "Client error corrects with default values when server fails to serialize required values."
+        appliesTo: "client"
+        tags: ["defaults", "error-correction"]
+        protocol: awsJson1_0
+        code: 200
+        bodyMediaType: "application/json"
+        headers: {"Content-Type": "application/x-amz-json-1.0"}
+        body: "{}"
+        params: {
+            requiredString: "hi"
+            requiredBoolean: true
+            requiredList: []
+            requiredTimestamp: 1
+            requiredBlob: "{}"
+            requiredByte: 1
+            requiredShort: 1
+            requiredInteger: 10
+            requiredLong: 100
+            requiredFloat: 1.0
+            requiredDouble: 1.0
+            requiredMap: {}
+            requiredEnum: "FOO"
+            requiredIntEnum: 1
+        }
+    }
+])
+
+operation OperationWithRequiredMembersWithDefaults {
+    output := with [RequiredMembersWithDefaultsMixin] {
+    }
+}
+
+@mixin
+structure RequiredMembersMixin {
+    @required
+    requiredString: String
+
+    @required
+    requiredBoolean: Boolean
+
+    @required
+    requiredList: RequiredStringList
+
+    @required
+    requiredTimestamp: Timestamp
+
+    @required
+    requiredBlob: Blob
+
+    @required
+    requiredByte: Byte
+
+    @required
+    requiredShort: Short
+
+    @required
+    requiredInteger: Integer
+
+    @required
+    requiredLong: Long
+
+    @required
+    requiredFloat: Float
+
+    @required
+    requiredDouble: Double
+
+    @required
+    requiredMap: RequiredStringMap
+}
+
+@mixin
+structure RequiredMembersWithDefaultsMixin {
+    @required
+    requiredString: String = "hi"
+
+    @required
+    requiredBoolean: Boolean = true
+
+    @required
+    requiredList: RequiredStringList = []
+
+    @required
+    requiredTimestamp: Timestamp = 1
+
+    @required
+    requiredBlob: Blob = "{}"
+
+    @required
+    requiredByte: Byte = 1
+
+    @required
+    requiredShort: Short = 1
+
+    @required
+    requiredInteger: Integer = 10
+
+    @required
+    requiredLong: Long = 100
+
+    @required
+    requiredFloat: Float = 1.0
+
+    @required
+    requiredDouble: Double = 1.0
+
+    @required
+    requiredMap: RequiredStringMap = {}
+
+    @required
+    requiredEnum: RequiredEnum = "FOO"
+
+    @required
+    requiredIntEnum: RequiredIntEnum = 1
+}
+
+list RequiredStringList {
+    member: String
+}
+
+map RequiredStringMap {
+    key: String
+    value: String
+}
+
+enum RequiredEnum {
+    FOO
+    BAR
+    BAZ
+}
+
+intEnum RequiredIntEnum {
+    ONE = 1
+    TWO = 2
+}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/clientendpointdiscovery/endpoint-error.errors b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/clientendpointdiscovery/endpoint-error.errors
index be91fa94b..8dd18d709 100644
--- a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/clientendpointdiscovery/endpoint-error.errors
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/clientendpointdiscovery/endpoint-error.errors
@@ -1,2 +1,2 @@
-[DANGER] ns.foo#FooService: Services SHOULD define an error which indicates an endpoint is invalid. | ClientEndpointDiscovery
+[DANGER] ns.foo#FooService: Services SHOULD define an error which indicates an endpoint is invalid. | ClientEndpointDiscovery.MissingErrorDefinition
 [ERROR] ns.foo#GetObject: The operation `ns.foo#GetObject` is marked with `aws.api#clientDiscoveredEndpoint` and is bound to the service `ns.foo#BarService` but does not have the required error `ns.foo#InvalidEndpointError`. | ClientEndpointDiscovery
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/clientendpointdiscovery/no-operations.errors b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/clientendpointdiscovery/no-operations.errors
new file mode 100644
index 000000000..489668693
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/clientendpointdiscovery/no-operations.errors
@@ -0,0 +1,1 @@
+[WARNING] ns.foo#BazService: The service `ns.foo#BazService` is configured to use endpoint discovery, but has no operations bound with the `aws.api#clientDiscoveredEndpoint` trait. | ClientEndpointDiscovery.NoOperations
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/clientendpointdiscovery/no-operations.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/clientendpointdiscovery/no-operations.smithy
new file mode 100644
index 000000000..ba6dc7936
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/clientendpointdiscovery/no-operations.smithy
@@ -0,0 +1,66 @@
+$version: "2.0"
+
+namespace ns.foo
+
+use aws.api#clientEndpointDiscovery
+use aws.api#clientDiscoveredEndpoint
+
+@clientEndpointDiscovery(
+    operation: DescribeEndpoints,
+    error: InvalidEndpointError,
+)
+service BazService {
+    version: "2021-06-29",
+    operations: [DescribeEndpoints, GetObjectWithEndpointError],
+}
+
+operation DescribeEndpoints {
+    input: DescribeEndpointsInput,
+    output: DescribeEndpointsOutput,
+}
+
+@input
+structure DescribeEndpointsInput {
+    Operation: String,
+    Identifiers: Identifiers,
+}
+
+map Identifiers {
+    key: String,
+    value: String,
+}
+
+@output
+structure DescribeEndpointsOutput {
+    Endpoints: Endpoints,
+}
+
+list Endpoints {
+    member: Endpoint,
+}
+
+structure Endpoint {
+    Address: String,
+    CachePeriodInMinutes: Long,
+}
+
+operation GetObjectWithEndpointError {
+    input: GetObjectWithEndpointErrorInput,
+    output: GetObjectWithEndpointErrorOutput,
+    errors: [InvalidEndpointError],
+}
+
+@input
+structure GetObjectWithEndpointErrorInput {
+    @required
+    Id: String,
+}
+
+@output
+structure GetObjectWithEndpointErrorOutput {
+    Object: Blob,
+}
+
+@error("client")
+@httpError(421)
+structure InvalidEndpointError {}
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/clientendpointdiscovery/unbound-operations.errors b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/clientendpointdiscovery/unbound-operations.errors
new file mode 100644
index 000000000..f3954f880
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/clientendpointdiscovery/unbound-operations.errors
@@ -0,0 +1,2 @@
+[ERROR] ns.foo#GetObject: The operation `ns.foo#GetObject` is marked with `aws.api#clientDiscoveredEndpoint` and is bound to the service `ns.foo#BarService` but does not have the required error `ns.foo#InvalidEndpointError`. | ClientEndpointDiscovery
+[ERROR] ns.foo#BarService: The operation `ns.foo#DescribeEndpoints` must be bound to the service `(service: `ns.foo#BarService`)` to use it as the endpoint operation. | ClientEndpointDiscovery.UnboundOperation.DescribeEndpoints
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/clientendpointdiscovery/unbound-operations.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/clientendpointdiscovery/unbound-operations.smithy
new file mode 100644
index 000000000..083ca4fde
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/clientendpointdiscovery/unbound-operations.smithy
@@ -0,0 +1,97 @@
+$version: "2.0"
+
+namespace ns.foo
+
+use aws.api#clientEndpointDiscovery
+use aws.api#clientDiscoveredEndpoint
+
+// This DOES have an error, but it's not bound to the operations. This should
+// result in an ERROR.
+@clientEndpointDiscovery(
+    operation: DescribeEndpoints,
+    error: InvalidEndpointError,
+)
+service BarService {
+    version: "2021-06-29",
+    operations: [GetObject],
+}
+
+// This DOES have an error, and it IS bound to the operations. This should
+// not produce any validation events.
+@clientEndpointDiscovery(
+    operation: DescribeEndpoints,
+    error: InvalidEndpointError,
+)
+service BazService {
+    version: "2021-06-29",
+    operations: [DescribeEndpoints, GetObjectWithEndpointError],
+}
+
+operation DescribeEndpoints {
+    input: DescribeEndpointsInput,
+    output: DescribeEndpointsOutput,
+}
+
+@input
+structure DescribeEndpointsInput {
+    Operation: String,
+    Identifiers: Identifiers,
+}
+
+map Identifiers {
+    key: String,
+    value: String,
+}
+
+@output
+structure DescribeEndpointsOutput {
+    Endpoints: Endpoints,
+}
+
+list Endpoints {
+    member: Endpoint,
+}
+
+structure Endpoint {
+    Address: String,
+    CachePeriodInMinutes: Long,
+}
+
+@clientDiscoveredEndpoint(required: true)
+operation GetObject {
+    input: GetObjectInput,
+    output: GetObjectOutput,
+}
+
+@input
+structure GetObjectInput {
+    @required
+    Id: String,
+}
+
+@output
+structure GetObjectOutput {
+    Object: Blob,
+}
+
+@clientDiscoveredEndpoint(required: true)
+operation GetObjectWithEndpointError {
+    input: GetObjectWithEndpointErrorInput,
+    output: GetObjectWithEndpointErrorOutput,
+    errors: [InvalidEndpointError],
+}
+
+@input
+structure GetObjectWithEndpointErrorInput {
+    @required
+    Id: String,
+}
+
+@output
+structure GetObjectWithEndpointErrorOutput {
+    Object: Blob,
+}
+
+@error("client")
+@httpError(421)
+structure InvalidEndpointError {}
diff --git a/smithy-codegen-core/src/test/java/software/amazon/smithy/codegen/core/directed/CapturingIntegration.java b/smithy-codegen-core/src/test/java/software/amazon/smithy/codegen/core/directed/CapturingIntegration.java
new file mode 100644
index 000000000..73fe5997d
--- /dev/null
+++ b/smithy-codegen-core/src/test/java/software/amazon/smithy/codegen/core/directed/CapturingIntegration.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.codegen.core.directed;
+
+import software.amazon.smithy.model.node.Node;
+import software.amazon.smithy.model.node.ObjectNode;
+
+public class CapturingIntegration implements TestIntegration {
+    public ObjectNode integrationSettings = Node.objectNode();
+
+    @Override
+    public String name() {
+        return "capturing-integration";
+    }
+
+    @Override
+    public void configure(TestSettings settings, ObjectNode integrationSettings) {
+        this.integrationSettings = integrationSettings;
+    }
+}
diff --git a/smithy-codegen-core/src/test/java/software/amazon/smithy/codegen/core/directed/CodegenDirectorTest.java b/smithy-codegen-core/src/test/java/software/amazon/smithy/codegen/core/directed/CodegenDirectorTest.java
index 295633630..df320005b 100644
--- a/smithy-codegen-core/src/test/java/software/amazon/smithy/codegen/core/directed/CodegenDirectorTest.java
+++ b/smithy-codegen-core/src/test/java/software/amazon/smithy/codegen/core/directed/CodegenDirectorTest.java
@@ -19,6 +19,9 @@ import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.notNullValue;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -33,6 +36,7 @@ import software.amazon.smithy.codegen.core.WriterDelegator;
 import software.amazon.smithy.model.Model;
 import software.amazon.smithy.model.node.ExpectationNotMetException;
 import software.amazon.smithy.model.node.Node;
+import software.amazon.smithy.model.node.ObjectNode;
 import software.amazon.smithy.model.shapes.ShapeId;
 
 public class CodegenDirectorTest {
@@ -42,6 +46,8 @@ public class CodegenDirectorTest {
         public final List<ShapeId> generatedEnumTypeEnums = new ArrayList<>();
         public final List<ShapeId> generatedStringTypeEnums = new ArrayList<>();
 
+        public final List<TestIntegration> integrations = new ArrayList<>();
+
         @Override
         public SymbolProvider createSymbolProvider(CreateSymbolProviderDirective<TestSettings> directive) {
             return shape -> Symbol.builder()
@@ -52,6 +58,8 @@ public class CodegenDirectorTest {
 
         @Override
         public TestContext createContext(CreateContextDirective<TestSettings, TestIntegration> directive) {
+            integrations.clear();
+            integrations.addAll(directive.integrations());
             WriterDelegator<TestWriter> delegator = new WriterDelegator<>(
                     directive.fileManifest(),
                     directive.symbolProvider(),
@@ -339,4 +347,42 @@ public class CodegenDirectorTest {
                 ShapeId.from("smithy.example#Foo")
         ));
     }
+
+    @Test
+    public void testConfiguresIntegrations() {
+        TestDirected testDirected = new TestDirected();
+        CodegenDirector<TestWriter, TestIntegration, TestContext, TestSettings> runner
+            = new CodegenDirector<>();
+        FileManifest manifest = new MockManifest();
+        Model model = Model.assembler()
+            .addImport(getClass().getResource("needs-sorting.smithy"))
+            .assemble()
+            .unwrap();
+
+        ObjectNode integrationSettings = Node.objectNode().withMember("spam", "eggs");
+        ObjectNode allIntegrationSettings = Node.objectNode()
+                .withMember("capturing-integration", integrationSettings);
+        ObjectNode settings = Node.objectNode()
+            .withMember("foo", "hi")
+            .withMember("integrations", allIntegrationSettings);
+        runner.settings(TestSettings.class, settings);
+        runner.directedCodegen(testDirected);
+        runner.fileManifest(manifest);
+        runner.service(ShapeId.from("smithy.example#Foo"));
+        runner.model(model);
+        runner.integrationClass(TestIntegration.class);
+        runner.performDefaultCodegenTransforms();
+        runner.shapeGenerationOrder(ShapeGenerationOrder.NONE);
+        runner.run();
+
+        assertThat(testDirected.integrations, not(empty()));
+        CapturingIntegration capturingIntegration = null;
+        for (TestIntegration integration : testDirected.integrations) {
+            if (integration instanceof CapturingIntegration) {
+                capturingIntegration = (CapturingIntegration) integration;
+            }
+        }
+        assertThat(capturingIntegration, notNullValue());
+        assertThat(capturingIntegration.integrationSettings, equalTo(integrationSettings));
+    }
 }
diff --git a/smithy-codegen-core/src/test/resources/META-INF/services/software.amazon.smithy.codegen.core.directed.TestIntegration b/smithy-codegen-core/src/test/resources/META-INF/services/software.amazon.smithy.codegen.core.directed.TestIntegration
new file mode 100644
index 000000000..7b3cf821d
--- /dev/null
+++ b/smithy-codegen-core/src/test/resources/META-INF/services/software.amazon.smithy.codegen.core.directed.TestIntegration
@@ -0,0 +1,1 @@
+software.amazon.smithy.codegen.core.directed.CapturingIntegration
diff --git a/smithy-model/src/test/java/software/amazon/smithy/model/ModelTest.java b/smithy-model/src/test/java/software/amazon/smithy/model/ModelTest.java
index 0a780d5fd..da572e985 100644
--- a/smithy-model/src/test/java/software/amazon/smithy/model/ModelTest.java
+++ b/smithy-model/src/test/java/software/amazon/smithy/model/ModelTest.java
@@ -30,6 +30,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
+import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -38,13 +39,10 @@ import software.amazon.smithy.model.knowledge.KnowledgeIndex;
 import software.amazon.smithy.model.knowledge.TopDownIndex;
 import software.amazon.smithy.model.node.ExpectationNotMetException;
 import software.amazon.smithy.model.node.Node;
-import software.amazon.smithy.model.shapes.EnumShape;
-import software.amazon.smithy.model.shapes.IntegerShape;
-import software.amazon.smithy.model.shapes.ListShape;
-import software.amazon.smithy.model.shapes.Shape;
-import software.amazon.smithy.model.shapes.ShapeId;
-import software.amazon.smithy.model.shapes.StringShape;
-import software.amazon.smithy.model.shapes.TimestampShape;
+import software.amazon.smithy.model.node.ObjectNode;
+import software.amazon.smithy.model.node.StringNode;
+import software.amazon.smithy.model.shapes.*;
+import software.amazon.smithy.model.traits.ExamplesTrait;
 import software.amazon.smithy.model.traits.TraitDefinition;
 import software.amazon.smithy.model.traits.synthetic.OriginalShapeIdTrait;
 
@@ -79,6 +77,26 @@ public class ModelTest {
         assertThat(modelA, not(equalTo(null)));
     }
 
+    @Test
+    public void modelEqualityExamples() {
+        StructureShape opInput = StructureShape.builder().id(ShapeId.fromParts("foo", "FooInput")).addMember("test", ShapeId.from("smithy.api#String")).build();
+        Supplier<OperationShape> op = () -> {
+            ExamplesTrait.Example example = ExamplesTrait.Example.builder().title("anything").input(ObjectNode.builder().withMember("test", StringNode.from("something")).build()).build();
+            ExamplesTrait examples = ExamplesTrait.builder().addExample(example).build();
+            return OperationShape.builder().id(ShapeId.fromParts("foo", "Foo")).input(opInput).addTrait(examples).build();
+        };
+
+        Model modelA = Model.builder()
+                .addShape(op.get())
+                .build();
+        Model modelB = Model.builder()
+                .addShape(op.get())
+                .build();
+
+        assertThat(modelA, equalTo(modelA));
+        assertThat(modelA, equalTo(modelB));
+    }
+
     @Test
     public void successfullyExpectsShapesOfType() {
         StringShape shape = StringShape.builder().id("ns.foo#A").build();