@Override
    public List<ValidationEvent> validate(Model model) {
        List<ValidationEvent> events = new ArrayList<>();

        // Normal shapes are expected to be upper camel.
        model.shapes()
                .filter(FunctionalUtils.not(Shape::isMemberShape))
                .filter(shape -> !shape.hasTrait(TraitDefinition.ID))
                .filter(shape -> !MemberNameHandling.UPPER.getRegex().matcher(shape.getId().getName()).find())
                .map(shape -> danger(shape,
                        format(
                                "%s shape name, `%s`, is not %s camel case",
                                shape.getType(),
                                shape.getId().getName(),
                                MemberNameHandling.UPPER)))
                .forEach(events::add);

        // Trait shapes are expected to be lower camel.
        model.shapes()
                .filter(shape -> shape.hasTrait(TraitDefinition.ID))
                .filter(shape -> !shape.hasTrait(AuthDefinitionTrait.ID))
                .filter(shape -> !shape.hasTrait(ProtocolDefinitionTrait.ID))
                .filter(shape -> !MemberNameHandling.LOWER.getRegex().matcher(shape.getId().getName()).find())
                .map(shape -> danger(shape,
                        format(
                                "%s trait definition, `%s`, is not lower camel case",
                                shape.getType(),
                                shape.getId().getName())))
                .forEach(events::add);

        // First validate each service's closure's member shape member names
        Set<MemberShape> seenShapes = new HashSet<>();
        for (ServiceShape serviceShape : model.getServiceShapes()) {
            Set<Shape> serviceClosure = new HashSet<>();
            Walker walker = new Walker(model);
            walker.iterateShapes(serviceShape).forEachRemaining(serviceClosure::add);
            List<MemberShape> memberShapes = serviceClosure.stream()
                    .filter(Shape::isMemberShape)
                    .map(shape -> (MemberShape) shape)
                    .collect(Collectors.toList());
            events.addAll(validateCamelCasing(model, memberShapes, serviceShape.getId().getName()));
            seenShapes.addAll(memberShapes);
        }

        // Next get all other member shapes (ex. trait shape members) and validate per namespace grouping
        Map<String, List<MemberShape>> memberShapesByNamespace = model.toSet(MemberShape.class)
                .stream()
                .filter(memberShape -> !seenShapes.contains(memberShape))
                .collect(Collectors.groupingBy(
                        memberShape -> memberShape.getContainer().getNamespace()));

        for (Map.Entry<String, List<MemberShape>> memberShapeGrouping : memberShapesByNamespace.entrySet()) {
            events.addAll(validateCamelCasing(model,
                    memberShapeGrouping.getValue(),
                    memberShapeGrouping.getKey() + " namespace"));
        }

        return events;
    }
Member names are serialized using either all lower camel or all upper camel, but not a mixture.
smithy-linters/src/main/java/software/amazon/smithy/linters/CamelCaseValidator.java