diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e25e1cf955c4b1686607126588fb2a38c3ebfb8b..0c58e330ceb5a558ad6a24168fd1daee052fa5fc 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -28,7 +28,7 @@ workflow:
   image: ${DEVOPS_REGISTRY}usgs/java:11-jdk
   stage: build
   tags:
-    - development
+    - build
 
 ####
 # Stage: init
@@ -88,7 +88,7 @@ Markdown Lint:
 Unit Tests:
   artifacts:
     paths:
-      - ${REPORTS_DIR}
+      - ${JACOCO_HTML_DIR}
     reports:
       junit: ${JUNIT_FILES}
   coverage: '/Total.*?([0-9]{1,3})%/'
@@ -99,11 +99,12 @@ Unit Tests:
   rules:
     -
       changes:
-        - 'src/**'
+        - 'src/**/*'
         - '*gradle*'
       when: on_success
+      allow_failure: false
     -
-      allow_failure: true
+      allow_failure: false
       when: manual
   script:
     - ./gradlew check
diff --git a/build.gradle b/build.gradle
index ad1ea19ea2ff3cccf80c7104c236c0d97554c619..2e50899d4d2bcb0f8728034732501647c4936ea6 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
 plugins {
   id "application"
-  id "com.diffplug.gradle.spotless" version "${spotlessVersion}"
+  id "com.diffplug.spotless" version "${spotlessVersion}"
   id "com.github.johnrengelman.shadow" version "${shadowVersion}"
   id "com.github.node-gradle.node" version "${nodeVersion}"
   id "com.github.spotbugs" version "${spotbugsVersion}"
@@ -8,7 +8,7 @@ plugins {
   id "eclipse-wtp"
   id "io.micronaut.application" version "${mnPluginVersion}"
   id "jacoco"
-  id "java"
+  id "java-library"
   id "maven-publish"
 }
 
@@ -21,6 +21,10 @@ configurations {
   nshmp
 }
 
+java {
+  withSourcesJar()
+}
+
 ext {
   libsDir = "libs"
   nshmpLib = "${libsDir}/nshmp-lib-artifacts"
diff --git a/gradle.properties b/gradle.properties
index f228ebc8627d515ee961965af46311e48fa93045..35dc381053e6ac70f9627725c2461b4a51b49e33 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,14 +1,14 @@
 githooksVersion = 1.2.0
 jacksonVersion = 2.9.0
-junitVersion = 5.5.2
+junitVersion = 5.8.2
 logbackVersion = 1.2.3
 micronautVersion = 2.4.1
 mnPluginVersion = 1.4.2
 nodeVersion = 3.0.1
 nshmFaultSectionsTag = v0.1
-nshmpLibVersion = 0.4.2
+nshmpLibVersion = 0.8.0
 nshmpWsUtilsVersion = 0.1.2
 shadowVersion = 6.1.0
-spotbugsVersion = 4.2.4
-spotlessVersion = 4.1.0
+spotbugsVersion = 4.7.0
+spotlessVersion = 6.0.4
 swaggerVersion = 2.1.7
diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle
index abf065f3003fac0999ed631822c51f7e66c7ecbc..93d3033d0b3ddedefdbe6bd7918bc2b7edbdafee 100644
--- a/gradle/dependencies.gradle
+++ b/gradle/dependencies.gradle
@@ -5,10 +5,10 @@ dependencies {
   nshmp "ghsc:nshmp-lib:${nshmpLibVersion}@zip"
 
   // Micronaut
-  annotationProcessor platform("io.micronaut:micronaut-bom")
+  annotationProcessor platform("io.micronaut:micronaut-bom:${micronautVersion}")
   annotationProcessor "io.micronaut:micronaut-inject-java"
   annotationProcessor "io.micronaut:micronaut-validation"
-  implementation platform("io.micronaut:micronaut-bom")
+  implementation platform("io.micronaut:micronaut-bom:${micronautVersion}")
   implementation "io.micronaut:micronaut-http-client"
   implementation "io.micronaut:micronaut-inject"
   implementation "io.micronaut:micronaut-validation"
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index be52383ef49cdf484098989f96738b3d82d7810d..84d1f85fd658134f7e982ae908fd537188f20bfe 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/gmm/GroundMotions.java b/src/main/java/gov/usgs/earthquake/nshmp/www/gmm/GroundMotions.java
index 5c986750cadefbb93b0434b3804ccfca3bba2412..a2b1925b76f6fc40e42fa701db53853618ffe0c9 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/www/gmm/GroundMotions.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/www/gmm/GroundMotions.java
@@ -18,11 +18,14 @@ import java.util.stream.Collectors;
 import com.google.common.primitives.Doubles;
 
 import gov.usgs.earthquake.nshmp.Maths;
+import gov.usgs.earthquake.nshmp.data.DoubleData;
 import gov.usgs.earthquake.nshmp.gmm.Gmm;
 import gov.usgs.earthquake.nshmp.gmm.GmmInput;
+import gov.usgs.earthquake.nshmp.gmm.GroundMotion;
 import gov.usgs.earthquake.nshmp.gmm.GroundMotionModel;
 import gov.usgs.earthquake.nshmp.gmm.Imt;
-import gov.usgs.earthquake.nshmp.gmm.ScalarGroundMotion;
+import gov.usgs.earthquake.nshmp.tree.Branch;
+import gov.usgs.earthquake.nshmp.tree.LogicTree;
 
 public class GroundMotions {
 
@@ -115,7 +118,7 @@ public class GroundMotions {
 
       GroundMotionModel model = gmm.instance(imt);
       for (GmmInput gmmInput : gmmInputs) {
-        ScalarGroundMotion gm = model.calc(gmmInput);
+        GroundMotion gm = combine(model.calc(gmmInput));
         means.add(gm.mean());
         sigmas.add(gm.sigma());
       }
@@ -131,6 +134,30 @@ public class GroundMotions {
         xsMap);
   }
 
+  @Deprecated
+  static GroundMotion combine(LogicTree<GroundMotion> tree) {
+    // once GroundMotions in lib is exposed remove this
+    if (tree.size() == 1) {
+      return tree.get(0).value();
+    }
+    double[] means = tree.stream()
+        .map(Branch::value)
+        .mapToDouble(GroundMotion::mean)
+        .toArray();
+    double[] sigmas = tree.stream()
+        .map(Branch::value)
+        .mapToDouble(GroundMotion::sigma)
+        .toArray();
+    double[] weights = tree.stream()
+        .mapToDouble(Branch::weight)
+        .toArray();
+    double mean = DoubleData.weightedSumLn(means, weights);
+    double sigma = DoubleData.weightedSum(sigmas, weights);
+    // TODO update NGA-East test results
+    // double sigma = Maths.srssWeighted(sigmas, weights);
+    return GroundMotion.create(mean, sigma);
+  }
+
   /*
    * Compute distance metrics for a fault.
    */
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/gmm/ResponseSpectra.java b/src/main/java/gov/usgs/earthquake/nshmp/www/gmm/ResponseSpectra.java
index e268119107ab09ad21dfe341b997597d7f834731..e1efc860349c1a027a89004428db13f622ca9f76 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/www/gmm/ResponseSpectra.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/www/gmm/ResponseSpectra.java
@@ -12,9 +12,9 @@ import com.google.common.collect.Maps;
 
 import gov.usgs.earthquake.nshmp.gmm.Gmm;
 import gov.usgs.earthquake.nshmp.gmm.GmmInput;
+import gov.usgs.earthquake.nshmp.gmm.GroundMotion;
 import gov.usgs.earthquake.nshmp.gmm.GroundMotionModel;
 import gov.usgs.earthquake.nshmp.gmm.Imt;
-import gov.usgs.earthquake.nshmp.gmm.ScalarGroundMotion;
 
 /**
  * Entry point for computing deterministic response spectra.
@@ -46,8 +46,9 @@ public class ResponseSpectra {
    * @return a two-element double[] containing the natural log of the median
    *         ground motion and its standard deviation
    */
+  @Deprecated
   public static double[] groundMotion(Gmm model, Imt imt, GmmInput source) {
-    ScalarGroundMotion sgm = model.instance(imt).calc(source);
+    GroundMotion sgm = GroundMotions.combine(model.instance(imt).calc(source));
     return new double[] { sgm.mean(), sgm.sigma() };
   }
 
@@ -71,7 +72,7 @@ public class ResponseSpectra {
     Result spectrum = new Result(imts.size());
     int i = 0;
     for (Imt imt : imts) {
-      ScalarGroundMotion sgm = model.instance(imt).calc(input);
+      GroundMotion sgm = GroundMotions.combine(model.instance(imt).calc(input));
       spectrum.periods[i] = imt.period();
       spectrum.means[i] = sgm.mean();
       spectrum.sigmas[i] = sgm.sigma();
@@ -145,12 +146,12 @@ public class ResponseSpectra {
       ImmutableList.Builder<Double> means = ImmutableList.builder();
       ImmutableList.Builder<Double> sigmas = ImmutableList.builder();
 
-      ScalarGroundMotion pgaGm = gmm.instance(Imt.PGA).calc(input);
+      GroundMotion pgaGm = GroundMotions.combine(gmm.instance(Imt.PGA).calc(input));
       means.add(pgaGm.mean());
       sigmas.add(pgaGm.sigma());
 
       for (Imt imt : saImts) {
-        ScalarGroundMotion sgm = gmm.instance(imt).calc(input);
+        GroundMotion sgm = GroundMotions.combine(gmm.instance(imt).calc(input));
         means.add(sgm.mean());
         sigmas.add(sgm.sigma());
       }