diff --git a/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/ConstraintProcedureMapper.kt b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/ConstraintProcedureMapper.kt
new file mode 100644
index 0000000000..015aac62ae
--- /dev/null
+++ b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/ConstraintProcedureMapper.kt
@@ -0,0 +1,10 @@
+package gov.nasa.ammos.aerie.procedural.constraints
+
+import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue
+import gov.nasa.jpl.aerie.merlin.protocol.types.ValueSchema
+
+interface ConstraintProcedureMapper<T: Constraint> {
+  fun valueSchema(): ValueSchema
+  fun serialize(procedure: T): SerializedValue
+  fun deserialize(arguments: SerializedValue): T
+}
diff --git a/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/annotations/ConstraintProcedure.kt b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/annotations/ConstraintProcedure.kt
new file mode 100644
index 0000000000..dea62c3745
--- /dev/null
+++ b/procedural/constraints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/annotations/ConstraintProcedure.kt
@@ -0,0 +1,3 @@
+package gov.nasa.ammos.aerie.procedural.constraints.annotations
+
+annotation class ConstraintProcedure
diff --git a/procedural/examples/foo-procedures/build.gradle b/procedural/examples/foo-procedures/build.gradle
index 721e290833..8cfd25c224 100644
--- a/procedural/examples/foo-procedures/build.gradle
+++ b/procedural/examples/foo-procedures/build.gradle
@@ -73,6 +73,52 @@ tasks.create("generateSchedulingProcedureJarTasks") {
   }
 }
 
+tasks.register('buildAllConstraintProcedureJars') {
+  group = 'ConstraintProcedureJars'
+
+  dependsOn "generateConstraintProcedureJarTasks"
+  dependsOn {
+    tasks.findAll { task -> task.name.startsWith('buildConstraintProcedureJar_') }
+  }
+}
+
+tasks.create("generateConstraintProcedureJarTasks") {
+  group = 'ConstraintProcedureJars'
+
+  final proceduresDir = findFirstMatchingBuildDir("generated/procedures")
+
+  if (proceduresDir == null) {
+    println "No procedures folder found"
+    return
+  }
+  println "Generating jar tasks for the following procedures directory: ${proceduresDir}"
+
+  final files = file(proceduresDir).listFiles()
+  if (files.length == 0) {
+    println "No procedures available within folder ${proceduresDir}"
+    return
+  }
+
+  files.toList().each { file ->
+    final nameWithoutExtension = file.name.replace(".java", "")
+    final taskName = "buildConstraintProcedureJar_${nameWithoutExtension}"
+
+    println "Generating ${taskName} task, which will build ${nameWithoutExtension}.jar"
+
+    tasks.create(taskName, ShadowJar) {
+      group = 'ConstraintProcedureJars'
+      configurations = [project.configurations.runtimeClasspath]
+      from sourceSets.main.output
+      archiveBaseName = "" // clear
+      archiveClassifier.set(nameWithoutExtension) // set output jar name
+      manifest {
+        attributes 'Main-Class': getMainClassFromGeneratedFile(file)
+      }
+      minimize()
+    }
+  }
+}
+
 private String findFirstMatchingBuildDir(String pattern) {
   String found = null
   final generatedDir = file("build/generated/sources")
diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/Helper.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/Helper.java
deleted file mode 100644
index 0308d22dd5..0000000000
--- a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/Helper.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package gov.nasa.ammos.aerie.procedural.examples.fooprocedures;
-
-public class Helper {
-  public static String greeting() {
-    return "hello from util";
-  }
-}
diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ConstFruit.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ConstFruit.java
index 762cc9f1fd..367f14e1b8 100644
--- a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ConstFruit.java
+++ b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ConstFruit.java
@@ -1,20 +1,22 @@
 package gov.nasa.ammos.aerie.procedural.examples.fooprocedures.constraints;
 
-import gov.nasa.ammos.aerie.procedural.constraints.GeneratorConstraint;
+import gov.nasa.ammos.aerie.procedural.constraints.Constraint;
 import gov.nasa.ammos.aerie.procedural.constraints.Violations;
+import gov.nasa.ammos.aerie.procedural.constraints.annotations.ConstraintProcedure;
 import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real;
 import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan;
 import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulationResults;
 import org.jetbrains.annotations.NotNull;
 
-public class ConstFruit extends GeneratorConstraint {
+@ConstraintProcedure
+public record ConstFruit() implements Constraint {
   @Override
-  public void generate(@NotNull Plan plan, @NotNull SimulationResults simResults) {
+  public Violations run(@NotNull Plan plan, @NotNull SimulationResults simResults) {
     final var fruit = simResults.resource("/fruit", Real.deserializer());
 
-    violate(Violations.on(
+    return Violations.on(
         fruit.equalTo(4),
         false
-    ));
+    );
   }
 }
diff --git a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/Hello.java b/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/Hello.java
deleted file mode 100644
index 1e36a1d2c1..0000000000
--- a/procedural/examples/foo-procedures/src/main/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/Hello.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package gov.nasa.ammos.aerie.procedural.examples.fooprocedures.constraints;
-
-import gov.nasa.ammos.aerie.procedural.examples.fooprocedures.Helper;
-
-class Hello {
-  public static void main(String[] args) {
-    System.out.println(Helper.greeting());
-  }
-}
diff --git a/procedural/processor/build.gradle b/procedural/processor/build.gradle
index b7c85efc1e..5699a5b50c 100644
--- a/procedural/processor/build.gradle
+++ b/procedural/processor/build.gradle
@@ -15,6 +15,7 @@ dependencies {
   implementation project(':merlin-sdk')
   implementation project(':contrib')
   implementation project(':procedural:scheduling')
+  implementation project(':procedural:constraints')
   implementation 'org.apache.commons:commons-lang3:3.13.0'
   implementation 'com.squareup:javapoet:1.13.0'
 }
diff --git a/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/AutoValueMappers.java b/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/AutoValueMappers.java
index b18232fc3f..0a17c8a7d7 100644
--- a/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/AutoValueMappers.java
+++ b/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/AutoValueMappers.java
@@ -59,7 +59,7 @@ static JavaFile generateAutoValueMappers(final ClassName typeName, final Iterabl
             .addAnnotation(
                 AnnotationSpec
                     .builder(javax.annotation.processing.Generated.class)
-                    .addMember("value", "$S", SchedulingProcedureProcessor.class.getCanonicalName())
+                    .addMember("value", "$S", ProcedureProcessor.class.getCanonicalName())
                     .build())
             .addAnnotation(
                 AnnotationSpec
diff --git a/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/SchedulingProcedureProcessor.java b/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/ProcedureProcessor.java
similarity index 70%
rename from procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/SchedulingProcedureProcessor.java
rename to procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/ProcedureProcessor.java
index 1b0ce144fd..f8771f3266 100644
--- a/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/SchedulingProcedureProcessor.java
+++ b/procedural/processor/src/main/java/gov/nasa/ammos/aerie/procedural/processor/ProcedureProcessor.java
@@ -9,10 +9,13 @@
 import gov.nasa.jpl.aerie.merlin.framework.ValueMapper;
 import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue;
 import gov.nasa.jpl.aerie.merlin.protocol.types.ValueSchema;
-import gov.nasa.ammos.aerie.procedural.scheduling.ProcedureMapper;
+import gov.nasa.ammos.aerie.procedural.scheduling.SchedulingProcedureMapper;
 import gov.nasa.ammos.aerie.procedural.scheduling.annotations.SchedulingProcedure;
 import gov.nasa.ammos.aerie.procedural.scheduling.annotations.WithMappers;
 
+import gov.nasa.ammos.aerie.procedural.constraints.ConstraintProcedureMapper;
+import gov.nasa.ammos.aerie.procedural.constraints.annotations.ConstraintProcedure;
+
 import javax.annotation.processing.Completion;
 import javax.annotation.processing.Filer;
 import javax.annotation.processing.Messager;
@@ -42,8 +45,11 @@
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 
-public final class SchedulingProcedureProcessor implements Processor {
+public final class ProcedureProcessor implements Processor {
   // Effectively final, late-initialized
   private Messager messager = null;
   private Filer filer = null;
@@ -58,7 +64,11 @@ public Set<String> getSupportedOptions() {
   /** Elements marked by these annotations will be treated as processing roots. */
   @Override
   public Set<String> getSupportedAnnotationTypes() {
-    return Set.of(SchedulingProcedure.class.getCanonicalName(), WithMappers.class.getCanonicalName());
+    return Set.of(
+        SchedulingProcedure.class.getCanonicalName(),
+        ConstraintProcedure.class.getCanonicalName(),
+        WithMappers.class.getCanonicalName()
+    );
   }
 
   @Override
@@ -104,26 +114,32 @@ public boolean process(final Set<? extends TypeElement> annotations, final Round
       typeRules.addAll(parseValueMappers(factory));
     }
 
-    final var procedures = roundEnv.getElementsAnnotatedWith(SchedulingProcedure.class);
+    final var schedulingProcedures = roundEnv.getElementsAnnotatedWith(SchedulingProcedure.class);
+    final var constraintProcedures = roundEnv.getElementsAnnotatedWith(ConstraintProcedure.class);
 
     final var generatedClassName = ClassName.get(packageElement.getQualifiedName() + ".generated", "AutoValueMappers");
-    for (final var procedure : procedures) {
+    for (final var procedure : schedulingProcedures) {
+      final var procedureElement = (TypeElement) procedure;
+      typeRules.add(AutoValueMappers.recordTypeRule(procedureElement, generatedClassName));
+    }
+
+    for (final var procedure : constraintProcedures) {
       final var procedureElement = (TypeElement) procedure;
       typeRules.add(AutoValueMappers.recordTypeRule(procedureElement, generatedClassName));
     }
 
     final var generatedFiles = new ArrayList<JavaFile>();
 
-    generatedFiles.add(AutoValueMappers.generateAutoValueMappers(generatedClassName, procedures, List.of()));
+    final var allProcedures = Stream.concat(schedulingProcedures.stream(),constraintProcedures.stream()).collect(Collectors.toSet());
+
+    generatedFiles.add(AutoValueMappers.generateAutoValueMappers(generatedClassName, allProcedures, List.of()));
 
     // For each procedure, generate a file that implements Procedure, Supplier<ValueMapper>
-    for (final var procedure : procedures) {
+    for (final var procedure : schedulingProcedures) {
       final TypeName procedureType = TypeName.get(procedure.asType());
-      final ParameterizedTypeName valueMapperType = ParameterizedTypeName.get(
-          ClassName.get(ValueMapper.class),
-          procedureType);
 
-      final var valueMapperCode = new Resolver(typeUtils, elementUtils, typeRules).applyRules(new TypePattern.ClassPattern(ClassName.get(ValueMapper.class), List.of(new TypePattern.ClassPattern((ClassName) procedureType, List.of()))));
+      final var valueMapperCode = new Resolver(typeUtils, elementUtils, typeRules)
+          .applyRules(new TypePattern.ClassPattern(ClassName.get(ValueMapper.class), List.of(new TypePattern.ClassPattern((ClassName) procedureType, List.of()))));
       if (valueMapperCode.isEmpty()) throw new Error("Could not generate a valuemapper for procedure " + procedure.getSimpleName());
 
 
@@ -131,7 +147,7 @@ public boolean process(final Set<? extends TypeElement> annotations, final Round
           .builder(generatedClassName.packageName() + ".procedures", TypeSpec
               .classBuilder(procedure.getSimpleName().toString())
               .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
-              .addSuperinterface(ParameterizedTypeName.get(ClassName.get(ProcedureMapper.class), procedureType))
+              .addSuperinterface(ParameterizedTypeName.get(ClassName.get(SchedulingProcedureMapper.class), procedureType))
               .addMethod(MethodSpec
                              .methodBuilder("valueSchema")
                              .addModifiers(Modifier.PUBLIC)
@@ -160,6 +176,52 @@ public boolean process(final Set<? extends TypeElement> annotations, final Round
           .build());
     }
 
+    // For each procedure, generate a file that implements Procedure, Supplier<ValueMapper>
+    for (final var procedure : constraintProcedures) {
+      final TypeName procedureType = TypeName.get(procedure.asType());
+
+      this.messager.printMessage(
+          Diagnostic.Kind.NOTE,
+          "Looking at: " + procedure.toString());
+
+      final var valueMapperCode = new Resolver(typeUtils, elementUtils, typeRules)
+          .applyRules(new TypePattern.ClassPattern(ClassName.get(ValueMapper.class), List.of(new TypePattern.ClassPattern((ClassName) procedureType, List.of()))));
+      if (valueMapperCode.isEmpty()) throw new Error("Could not generate a valuemapper for procedure " + procedure.getSimpleName());
+
+
+      generatedFiles.add(JavaFile
+                             .builder(generatedClassName.packageName() + ".procedures", TypeSpec
+                                 .classBuilder(procedure.getSimpleName().toString())
+                                 .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+                                 .addSuperinterface(ParameterizedTypeName.get(ClassName.get(ConstraintProcedureMapper.class), procedureType))
+                                 .addMethod(MethodSpec
+                                                .methodBuilder("valueSchema")
+                                                .addModifiers(Modifier.PUBLIC)
+                                                .addAnnotation(Override.class)
+                                                .returns(ValueSchema.class)
+                                                .addStatement("return $L.getValueSchema()", valueMapperCode.get())
+                                                .build())
+                                 .addMethod(MethodSpec
+                                                .methodBuilder("serialize")
+                                                .addModifiers(Modifier.PUBLIC)
+                                                .addAnnotation(Override.class)
+                                                .addParameter(procedureType, "procedure")
+                                                .returns(SerializedValue.class)
+                                                .addStatement("return $L.serializeValue(procedure)", valueMapperCode.get())
+                                                .build())
+                                 .addMethod(MethodSpec
+                                                .methodBuilder("deserialize")
+                                                .addModifiers(Modifier.PUBLIC)
+                                                .addAnnotation(Override.class)
+                                                .addParameter(SerializedValue.class, "value")
+                                                .returns(procedureType)
+                                                .addStatement("return $L.deserializeValue(value).getSuccessOrThrow(e -> new $T(e))", valueMapperCode.get(), RuntimeException.class)
+                                                .build())
+                                 .build())
+                             .skipJavaLangImports(true)
+                             .build());
+    }
+
     for (final var generatedFile : generatedFiles) {
       this.messager.printMessage(
           Diagnostic.Kind.NOTE,
diff --git a/procedural/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/procedural/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
index 13dd2f6a19..e848258d5c 100644
--- a/procedural/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
+++ b/procedural/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -1 +1 @@
-gov.nasa.ammos.aerie.procedural.processor.SchedulingProcedureProcessor
+gov.nasa.ammos.aerie.procedural.processor.ProcedureProcessor
diff --git a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/ProcedureMapper.kt b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/SchedulingProcedureMapper.kt
similarity index 86%
rename from procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/ProcedureMapper.kt
rename to procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/SchedulingProcedureMapper.kt
index 3340317cb3..efbed06b69 100644
--- a/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/ProcedureMapper.kt
+++ b/procedural/scheduling/src/main/kotlin/gov/nasa/ammos/aerie/procedural/scheduling/SchedulingProcedureMapper.kt
@@ -3,7 +3,7 @@ package gov.nasa.ammos.aerie.procedural.scheduling
 import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue
 import gov.nasa.jpl.aerie.merlin.protocol.types.ValueSchema
 
-interface ProcedureMapper<T: Goal> {
+interface SchedulingProcedureMapper<T: Goal> {
   fun valueSchema(): ValueSchema
   fun serialize(procedure: T): SerializedValue
   fun deserialize(arguments: SerializedValue): T
diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/ProcedureLoader.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/ProcedureLoader.java
index 50a46ec1e8..3a308b7825 100644
--- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/ProcedureLoader.java
+++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/ProcedureLoader.java
@@ -1,7 +1,6 @@
 package gov.nasa.jpl.aerie.scheduler;
 
-import gov.nasa.jpl.aerie.merlin.protocol.types.ValueSchema;
-import gov.nasa.ammos.aerie.procedural.scheduling.ProcedureMapper;
+import gov.nasa.ammos.aerie.procedural.scheduling.SchedulingProcedureMapper;
 
 import java.io.IOException;
 import java.net.MalformedURLException;
@@ -12,7 +11,7 @@
 import java.util.jar.JarFile;
 
 public final class ProcedureLoader {
-  public static ProcedureMapper<?> loadProcedure(final Path path)
+  public static SchedulingProcedureMapper<?> loadProcedure(final Path path)
   throws ProcedureLoadException
   {
     final var className = getImplementingClassName(path);
@@ -20,11 +19,11 @@ public static ProcedureMapper<?> loadProcedure(final Path path)
 
     try {
       final var pluginClass$ = classLoader.loadClass(className);
-      if (!ProcedureMapper.class.isAssignableFrom(pluginClass$)) {
+      if (!SchedulingProcedureMapper.class.isAssignableFrom(pluginClass$)) {
         throw new ProcedureLoadException(path);
       }
 
-      return (ProcedureMapper<?>) pluginClass$.getConstructor().newInstance();
+      return (SchedulingProcedureMapper<?>) pluginClass$.getConstructor().newInstance();
     } catch (final ReflectiveOperationException ex) {
       throw new ProcedureLoadException(path, ex);
     }
@@ -58,7 +57,7 @@ private ProcedureLoadException(final Path path, final Throwable cause) {
       super(
           String.format(
               "No implementation found for `%s` at path `%s`",
-              ProcedureMapper.class.getSimpleName(),
+              SchedulingProcedureMapper.class.getSimpleName(),
               path),
           cause);
     }
diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java
index 9495c91961..c777c77095 100644
--- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java
+++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/goals/Procedure.java
@@ -2,7 +2,7 @@
 
 import gov.nasa.jpl.aerie.merlin.driver.MissionModel;
 import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue;
-import gov.nasa.ammos.aerie.procedural.scheduling.ProcedureMapper;
+import gov.nasa.ammos.aerie.procedural.scheduling.SchedulingProcedureMapper;
 import gov.nasa.ammos.aerie.procedural.scheduling.plan.Edit;
 import gov.nasa.jpl.aerie.scheduler.DirectiveIdGenerator;
 import gov.nasa.jpl.aerie.scheduler.ProcedureLoader;
@@ -45,7 +45,7 @@ public void run(
       final SimulationFacade simulationFacade,
       final DirectiveIdGenerator idGenerator
   ) {
-    final ProcedureMapper<?> procedureMapper;
+    final SchedulingProcedureMapper<?> procedureMapper;
     try {
       procedureMapper = ProcedureLoader.loadProcedure(jarPath);
     } catch (ProcedureLoader.ProcedureLoadException e) {
diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/SpecificationService.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/SpecificationService.java
index 9fe6fad93e..21b66feb4b 100644
--- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/SpecificationService.java
+++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/SpecificationService.java
@@ -1,6 +1,6 @@
 package gov.nasa.jpl.aerie.scheduler.server.services;
 
-import gov.nasa.ammos.aerie.procedural.scheduling.ProcedureMapper;
+import gov.nasa.ammos.aerie.procedural.scheduling.SchedulingProcedureMapper;
 import gov.nasa.jpl.aerie.scheduler.ProcedureLoader;
 import gov.nasa.jpl.aerie.scheduler.server.exceptions.NoSuchSchedulingGoalException;
 import gov.nasa.jpl.aerie.scheduler.server.exceptions.NoSuchSpecificationException;
@@ -40,7 +40,7 @@ public void refreshSchedulingProcedureParameterTypes(long goalId, long revision)
         // Do nothing
       }
       case GoalType.JAR jar -> {
-        final ProcedureMapper<?> mapper;
+        final SchedulingProcedureMapper<?> mapper;
         try {
           mapper = ProcedureLoader.loadProcedure(Path.of("/usr/src/app/merlin_file_store", jar.path().toString()));
         } catch (ProcedureLoader.ProcedureLoadException e) {