diff --git a/.checkstyle/checkstyle.xml b/.checkstyle/checkstyle.xml
index bf884a0..402eedb 100644
--- a/.checkstyle/checkstyle.xml
+++ b/.checkstyle/checkstyle.xml
@@ -102,6 +102,7 @@
     
     
       
+      
     
 
     
diff --git a/build.gradle.kts b/build.gradle.kts
index 5711dd8..665e5ae 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -53,7 +53,9 @@ tasks.jar {
 }
 
 dependencies {
-  implementation(libs.mammoth)
+  implementation(libs.mammoth) {
+    exclude(group = "org.jetbrains", module = "annotations")
+  }
   "workerClasspathCompileOnly"(libs.pebble)
   "workerClasspathCompileOnly"(libs.snakeyamlEngine)
   "workerClasspathCompileOnly"(workerShared.map { it.output })
@@ -62,7 +64,9 @@ dependencies {
   privateRuntime.name(workerShared.map { it.output })
   compileOnly(libs.ideaExtPlugin)
 
-  testImplementation(libs.mammoth.test)
+  testImplementation(libs.mammoth.test) {
+    exclude(group = "org.jetbrains", module = "annotations")
+  }
   testImplementation(platform(libs.junit.bom))
   testImplementation(libs.junit.api)
   testRuntimeOnly(libs.junit.engine)
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 64d6b3c..8e3380b 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -6,7 +6,7 @@ checkstyle = "10.26.1"
 ideaExt = "1.1.10"
 indra = "3.1.3"
 junit = "5.13.4"
-mammoth = "1.4.0"
+mammoth = "1.5.0"
 pebble = "3.2.4"
 snakeyaml = "2.9"
 spotless = "7.0.4"
diff --git a/src/main/java/net/kyori/blossom/Blossom.java b/src/main/java/net/kyori/blossom/Blossom.java
index d83773e..4db78c6 100644
--- a/src/main/java/net/kyori/blossom/Blossom.java
+++ b/src/main/java/net/kyori/blossom/Blossom.java
@@ -44,10 +44,10 @@
 import org.gradle.plugins.ide.eclipse.model.EclipseModel;
 import org.gradle.plugins.ide.idea.model.IdeaModel;
 import org.gradle.util.GradleVersion;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
 import org.jetbrains.gradle.ext.ProjectSettings;
 import org.jetbrains.gradle.ext.TaskTriggersConfig;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
 
 /**
  * A template processor for Gradle projects.
@@ -56,6 +56,7 @@
  *
  * @since 2.0.0
  */
+@NullMarked
 public class Blossom implements ProjectPlugin {
   private static final String GENERATION_GROUP = "blossom";
   private static final String EXTENSION_NAME = "blossom";
@@ -65,10 +66,10 @@ public class Blossom implements ProjectPlugin {
 
   @Override
   public void apply(
-    final @NotNull Project project,
-    final @NotNull PluginContainer plugins,
-    final @NotNull ExtensionContainer extensions,
-    final @NotNull TaskContainer tasks
+    final Project project,
+    final PluginContainer plugins,
+    final ExtensionContainer extensions,
+    final TaskContainer tasks
   ) {
     plugins.withType(JavaBasePlugin.class, $ -> {
       final SetProperty outputDirs = project.getObjects().setProperty(File.class);
@@ -121,10 +122,10 @@ private void registerGenerateAllTask(final Project project, final TaskContainer
 
     IdeConfigurer.apply(project, new IdeConfigurer.IdeImportAction() {
       @Override
-      public void idea(final @NotNull Project project, final @NotNull IdeaModel idea, final @NotNull ProjectSettings ideaExtension) {
+      public void idea(final Project project, final IdeaModel idea, final ProjectSettings ideaExtension) {
         ((ExtensionAware) ideaExtension).getExtensions().getByType(TaskTriggersConfig.class).afterSync(generateTemplates);
         project.afterEvaluate(p -> {
-          final @Nullable IdeaModel projectIdea = p.getExtensions().getByType(IdeaModel.class);
+          final IdeaModel projectIdea = p.getExtensions().getByType(IdeaModel.class);
           if (projectIdea.getModule() != null) {
             projectIdea.getModule().getGeneratedSourceDirs().addAll(outputDirs.get());
           }
@@ -132,7 +133,7 @@ public void idea(final @NotNull Project project, final @NotNull IdeaModel idea,
       }
 
       @Override
-      public void eclipse(final @NotNull Project project, final @NotNull EclipseModel eclipse) {
+      public void eclipse(final Project project, final EclipseModel eclipse) {
         eclipse.synchronizationTasks(generateTemplates);
       }
     });
diff --git a/src/main/java/net/kyori/blossom/BlossomExtension.java b/src/main/java/net/kyori/blossom/BlossomExtension.java
index a94d4cb..34cc10b 100644
--- a/src/main/java/net/kyori/blossom/BlossomExtension.java
+++ b/src/main/java/net/kyori/blossom/BlossomExtension.java
@@ -24,7 +24,7 @@
 import org.gradle.api.Action;
 import org.gradle.api.NamedDomainObjectProvider;
 import org.gradle.api.PolymorphicDomainObjectContainer;
-import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
 
 import static java.util.Objects.requireNonNull;
 
@@ -35,6 +35,7 @@
  *
  * @since 2.0.0
  */
+@NullMarked
 public interface BlossomExtension {
   String RESOURCE_TEMPLATE_SET_NAME = "resource";
   String GROOVY_SOURCES_TEMPLATE_SET_NAME = "groovy";
@@ -48,7 +49,7 @@ public interface BlossomExtension {
    * @return the resource template set for configuration
    * @since 2.0.0
    */
-  default @NotNull NamedDomainObjectProvider resources() {
+  default NamedDomainObjectProvider resources() {
     return this.customResources(RESOURCE_TEMPLATE_SET_NAME);
   }
 
@@ -58,7 +59,7 @@ public interface BlossomExtension {
    * @param configureAction the action to perform on the primary resource template set
    * @since 2.0.0
    */
-  default void resources(final @NotNull Action super ResourceTemplateSet> configureAction) {
+  default void resources(final Action super ResourceTemplateSet> configureAction) {
     this.resources().configure(requireNonNull(configureAction, "configureAction"));
   }
 
@@ -71,7 +72,7 @@ default void resources(final @NotNull Action super ResourceTemplateSet> config
    * @return the resource template set for configuration
    * @since 2.1.0
    */
-  default @NotNull NamedDomainObjectProvider customResources(final @NotNull String setName) {
+  default NamedDomainObjectProvider customResources(final String setName) {
     requireNonNull(setName, "setName");
     if (this.getTemplateSets().getNames().contains(setName)) {
       return this.getTemplateSets().named(setName, ResourceTemplateSet.class);
@@ -89,7 +90,7 @@ default void resources(final @NotNull Action super ResourceTemplateSet> config
    * @param configureAction the action to perform on the primary resource template set
    * @since 2.1.0
    */
-  default void customResources(final @NotNull String setName, final @NotNull Action super ResourceTemplateSet> configureAction) {
+  default void customResources(final String setName, final Action super ResourceTemplateSet> configureAction) {
     this.customResources(setName).configure(requireNonNull(configureAction, "configureAction"));
   }
 
@@ -109,7 +110,7 @@ private NamedDomainObjectProvider registerSourceTemplateSet(f
    * @return the groovy source template set.
    * @since 2.0.0
    */
-  default @NotNull NamedDomainObjectProvider groovySources() {
+  default NamedDomainObjectProvider groovySources() {
     return this.registerSourceTemplateSet(GROOVY_SOURCES_TEMPLATE_SET_NAME, SourceTemplateSet::groovy);
   }
 
@@ -121,7 +122,7 @@ private NamedDomainObjectProvider registerSourceTemplateSet(f
    * @param configureAction the action to configure the set with
    * @since 2.0.0
    */
-  default void groovySources(final @NotNull Action super SourceTemplateSet> configureAction) {
+  default void groovySources(final Action super SourceTemplateSet> configureAction) {
     this.registerSourceTemplateSet(GROOVY_SOURCES_TEMPLATE_SET_NAME, SourceTemplateSet::groovy).configure(requireNonNull(configureAction, "configureAction"));
   }
 
@@ -133,7 +134,7 @@ default void groovySources(final @NotNull Action super SourceTemplateSet> conf
    * @return the java source template set.
    * @since 2.0.0
    */
-  default @NotNull NamedDomainObjectProvider javaSources() {
+  default NamedDomainObjectProvider javaSources() {
     return this.registerSourceTemplateSet(JAVA_SOURCES_TEMPLATE_SET_NAME, SourceTemplateSet::java);
   }
 
@@ -145,7 +146,7 @@ default void groovySources(final @NotNull Action super SourceTemplateSet> conf
    * @param configureAction the action to configure the set with
    * @since 2.0.0
    */
-  default void javaSources(final @NotNull Action super SourceTemplateSet> configureAction) {
+  default void javaSources(final Action super SourceTemplateSet> configureAction) {
     this.registerSourceTemplateSet(JAVA_SOURCES_TEMPLATE_SET_NAME, SourceTemplateSet::java).configure(requireNonNull(configureAction, "configureAction"));
   }
 
@@ -157,7 +158,7 @@ default void javaSources(final @NotNull Action super SourceTemplateSet> config
    * @return the kotlin source template set.
    * @since 2.0.0
    */
-  default @NotNull NamedDomainObjectProvider kotlinSources() {
+  default NamedDomainObjectProvider kotlinSources() {
     return this.registerSourceTemplateSet(KOTLIN_SOURCES_TEMPLATE_SET_NAME, SourceTemplateSet::kotlin);
   }
 
@@ -169,7 +170,7 @@ default void javaSources(final @NotNull Action super SourceTemplateSet> config
    * @param configureAction the action to configure the set with
    * @since 2.0.0
    */
-  default void kotlinSources(final @NotNull Action super SourceTemplateSet> configureAction) {
+  default void kotlinSources(final Action super SourceTemplateSet> configureAction) {
     this.registerSourceTemplateSet(KOTLIN_SOURCES_TEMPLATE_SET_NAME, SourceTemplateSet::kotlin).configure(requireNonNull(configureAction, "configureAction"));
   }
 
@@ -181,7 +182,7 @@ default void kotlinSources(final @NotNull Action super SourceTemplateSet> conf
    * @return the scala source template set.
    * @since 2.0.0
    */
-  default @NotNull NamedDomainObjectProvider scalaSources() {
+  default NamedDomainObjectProvider scalaSources() {
     return this.registerSourceTemplateSet(SCALA_SOURCES_TEMPLATE_SET_NAME, SourceTemplateSet::scala);
   }
 
@@ -193,7 +194,7 @@ default void kotlinSources(final @NotNull Action super SourceTemplateSet> conf
    * @param configureAction the action to configure the set with
    * @since 2.0.0
    */
-  default void scalaSources(final @NotNull Action super SourceTemplateSet> configureAction) {
+  default void scalaSources(final Action super SourceTemplateSet> configureAction) {
     this.registerSourceTemplateSet(SCALA_SOURCES_TEMPLATE_SET_NAME, SourceTemplateSet::scala).configure(requireNonNull(configureAction, "configureAction"));
   }
 
@@ -209,7 +210,7 @@ default void scalaSources(final @NotNull Action super SourceTemplateSet> confi
    * @return the source template set
    * @since 2.1.0
    */
-  default @NotNull NamedDomainObjectProvider customSources(final @NotNull String setName, final @NotNull Action super SourceTemplateSet> configureAction) {
+  default NamedDomainObjectProvider customSources(final String setName, final Action super SourceTemplateSet> configureAction) {
     requireNonNull(setName, "setName");
     final NamedDomainObjectProvider setProvider;
     if (this.getTemplateSets().getNames().contains(setName)) {
@@ -221,14 +222,13 @@ default void scalaSources(final @NotNull Action super SourceTemplateSet> confi
     return setProvider;
   }
 
-
   /**
    * Get all currently registered template sets for this source set.
    *
    * @return the template sets
    * @since 2.0.0
    */
-  @NotNull PolymorphicDomainObjectContainer getTemplateSets();
+  PolymorphicDomainObjectContainer getTemplateSets();
 
   /**
    * Configure template sets that apply to this source set.
@@ -236,7 +236,7 @@ default void scalaSources(final @NotNull Action super SourceTemplateSet> confi
    * @param configurer the action to perform
    * @since 2.0.0
    */
-  default void templateSets(final @NotNull Action> configurer) {
+  default void templateSets(final Action> configurer) {
     Configurable.configure(this.getTemplateSets(), configurer);
   }
 }
diff --git a/src/main/java/net/kyori/blossom/GenerateTemplates.java b/src/main/java/net/kyori/blossom/GenerateTemplates.java
index db2588a..bc75a9e 100644
--- a/src/main/java/net/kyori/blossom/GenerateTemplates.java
+++ b/src/main/java/net/kyori/blossom/GenerateTemplates.java
@@ -39,13 +39,14 @@
 import org.gradle.api.tasks.SkipWhenEmpty;
 import org.gradle.api.tasks.TaskAction;
 import org.gradle.workers.WorkerExecutor;
-import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
 
 /**
  * Generate real files based on templates and input parameters.
  *
  * @since 2.0.0
  */
+@NullMarked
 public abstract class GenerateTemplates extends DefaultTask {
 
   /**
@@ -55,7 +56,7 @@ public abstract class GenerateTemplates extends DefaultTask {
    * @since 2.0.0
    */
   @Nested
-  public abstract @NotNull Property getBaseSet();
+  public abstract Property getBaseSet();
 
   /**
    * Files that can be included in templates, but that are not themselves templates.
@@ -66,7 +67,7 @@ public abstract class GenerateTemplates extends DefaultTask {
    * @since 2.0.0
    */
   @InputFiles
-  protected abstract @NotNull ConfigurableFileCollection getIncludesDirectories();
+  protected abstract ConfigurableFileCollection getIncludesDirectories();
 
   /**
    * Source directory for templates to process.
@@ -76,7 +77,7 @@ public abstract class GenerateTemplates extends DefaultTask {
    */
   @InputFiles
   @SkipWhenEmpty
-  protected abstract @NotNull ConfigurableFileCollection getSourceDirectories();
+  protected abstract ConfigurableFileCollection getSourceDirectories();
 
   /**
    * Destination directory for template output.
@@ -85,7 +86,7 @@ public abstract class GenerateTemplates extends DefaultTask {
    * @since 2.0.0
    */
   @OutputDirectory
-  public abstract @NotNull DirectoryProperty getOutputDir();
+  public abstract DirectoryProperty getOutputDir();
 
   /**
    * The worker classpath. This should include Pebble and SnakeYAML engine.
diff --git a/src/main/java/net/kyori/blossom/ResourceTemplateSet.java b/src/main/java/net/kyori/blossom/ResourceTemplateSet.java
index dbd0994..7524a18 100644
--- a/src/main/java/net/kyori/blossom/ResourceTemplateSet.java
+++ b/src/main/java/net/kyori/blossom/ResourceTemplateSet.java
@@ -20,10 +20,13 @@
  */
 package net.kyori.blossom;
 
+import org.jspecify.annotations.NullMarked;
+
 /**
  * A template set type targeting resources.
  *
  * @since 2.0.0
  */
+@NullMarked
 public interface ResourceTemplateSet extends TemplateSet {
 }
diff --git a/src/main/java/net/kyori/blossom/SourceTemplateSet.java b/src/main/java/net/kyori/blossom/SourceTemplateSet.java
index 2707b8b..f713c25 100644
--- a/src/main/java/net/kyori/blossom/SourceTemplateSet.java
+++ b/src/main/java/net/kyori/blossom/SourceTemplateSet.java
@@ -20,13 +20,14 @@
  */
 package net.kyori.blossom;
 
-import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
 
 /**
  * A template set for sources.
  *
  * @since 2.0.0
  */
+@NullMarked
 public interface SourceTemplateSet extends TemplateSet {
   /**
    * Configure to generate templates contributing to Groovy sources.
@@ -68,5 +69,5 @@ default void scala() {
    * @param name the name of the language extension
    * @since 2.0.0
    */
-  void namedLanguageExtension(final @NotNull String name);
+  void namedLanguageExtension(final String name);
 }
diff --git a/src/main/java/net/kyori/blossom/TemplateSet.java b/src/main/java/net/kyori/blossom/TemplateSet.java
index 85fd002..4f93485 100644
--- a/src/main/java/net/kyori/blossom/TemplateSet.java
+++ b/src/main/java/net/kyori/blossom/TemplateSet.java
@@ -36,7 +36,7 @@
 import org.gradle.api.tasks.Nested;
 import org.gradle.api.tasks.Optional;
 import org.jetbrains.annotations.ApiStatus;
-import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
 
 /**
  * A set of templates associated with one source sets.
@@ -45,11 +45,12 @@
  *
  * @since 2.0.0
  */
+@NullMarked
 @ApiStatus.NonExtendable
 public interface TemplateSet extends Named {
   @Internal
   @Override
-  @NotNull String getName();
+  String getName();
 
   /**
    * Whether to strip trailing newlines from Pebble tokens that occur at the end of a line.
@@ -61,7 +62,7 @@ public interface TemplateSet extends Named {
    * @since 2.2.0
    */
   @Input
-  @NotNull Property getTrimNewlines();
+  Property getTrimNewlines();
 
   /**
    * A collection of data files in YAML format.
@@ -70,7 +71,7 @@ public interface TemplateSet extends Named {
    * @since 2.0.0
    */
   @InputFiles
-  @NotNull ConfigurableFileCollection getPropertyFiles();
+  ConfigurableFileCollection getPropertyFiles();
 
   /**
    * Add a data file for variable data.
@@ -78,7 +79,7 @@ public interface TemplateSet extends Named {
    * @param dataFile the data file to add, evaluated as per {@link org.gradle.api.Project#file(Object)}
    * @since 2.0.0
    */
-  default void propertyFile(final @NotNull Object dataFile) {
+  default void propertyFile(final Object dataFile) {
     this.getPropertyFiles().from(dataFile);
   }
 
@@ -91,7 +92,7 @@ default void propertyFile(final @NotNull Object dataFile) {
    * @since 2.0.0
    */
   @Input
-  @NotNull MapProperty getProperties();
+  MapProperty getProperties();
 
   /**
    * Set a single property for this template.
@@ -125,7 +126,7 @@ default void property(final String property, final Provider value) {
    */
   @Input
   @Optional
-  @NotNull Property getHeader();
+  Property getHeader();
 
   /**
    * A container of template variants.
@@ -136,7 +137,7 @@ default void property(final String property, final Provider value) {
    * @since 2.0.0
    */
   @Nested
-  @NotNull NamedDomainObjectContainer getVariants();
+  NamedDomainObjectContainer getVariants();
 
   /**
    * Register variants with certain names to be produced.
@@ -144,7 +145,7 @@ default void property(final String property, final Provider value) {
    * @param variants the variants to produce
    * @since 2.0.0
    */
-  default void variants(final @NotNull String@NotNull... variants) {
+  default void variants(final String... variants) {
     for (final String variant : variants) {
       this.getVariants().register(variant);
     }
@@ -156,7 +157,7 @@ default void variants(final @NotNull String@NotNull... variants) {
    * @param configureAction an action to configure variants
    * @since 2.0.0
    */
-  default void variants(final @NotNull Action> configureAction) {
+  default void variants(final Action> configureAction) {
     Configurable.configure(this.getVariants(), configureAction);
   }
 
@@ -169,7 +170,7 @@ default void variants(final @NotNull Action> confi
    * @since 2.0.0
    */
   @Internal
-  @NotNull SourceDirectorySet getTemplates();
+  SourceDirectorySet getTemplates();
 
   /**
    * Add one or more directories containing templates.
@@ -177,7 +178,7 @@ default void variants(final @NotNull Action> confi
    * @param templates the template directories to add, evaluated following {@link org.gradle.api.Project#files(Object...)}
    * @since 2.0.0
    */
-  default void templates(final @NotNull Object@NotNull... templates) {
+  default void templates(final Object... templates) {
     this.getTemplates().srcDirs(templates);
   }
 
@@ -188,7 +189,7 @@ default void templates(final @NotNull Object@NotNull... templates) {
    * @since 2.0.0
    */
   @Internal
-  @NotNull SourceDirectorySet getIncludes();
+  SourceDirectorySet getIncludes();
 
   /**
    * Add includes directories to the template path.
@@ -197,7 +198,7 @@ default void templates(final @NotNull Object@NotNull... templates) {
    * @see #getIncludes()
    * @since 2.0.0
    */
-  default void include(final @NotNull Object@NotNull... includes) {
+  default void include(final Object... includes) {
     this.getIncludes().srcDirs(includes);
   }
 }
diff --git a/src/main/java/net/kyori/blossom/Variant.java b/src/main/java/net/kyori/blossom/Variant.java
index 2378d3a..52e5ba5 100644
--- a/src/main/java/net/kyori/blossom/Variant.java
+++ b/src/main/java/net/kyori/blossom/Variant.java
@@ -28,7 +28,7 @@
 import org.gradle.api.tasks.Input;
 import org.gradle.api.tasks.InputFiles;
 import org.jetbrains.annotations.ApiStatus;
-import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
 
 /**
  * Template variant.
@@ -43,11 +43,12 @@
  *
  * @since 2.0.0
  */
+@NullMarked
 @ApiStatus.NonExtendable
 public interface Variant extends Named {
   @Override
   @Input
-  @NotNull String getName();
+  String getName();
 
   /**
    * Data files containing template parameters.
@@ -56,7 +57,7 @@ public interface Variant extends Named {
    * @since 2.0.0
    */
   @InputFiles
-  @NotNull ConfigurableFileCollection getPropertyFiles();
+  ConfigurableFileCollection getPropertyFiles();
 
   /**
    * Runtime properties for inserting into templates.
@@ -65,7 +66,7 @@ public interface Variant extends Named {
    * @since 2.0.0
    */
   @Input
-  @NotNull MapProperty getProperties();
+  MapProperty getProperties();
 
   /**
    * Register multiple properties for this variant.
@@ -73,7 +74,7 @@ public interface Variant extends Named {
    * @param configureAction action to configure properties
    * @since 2.0.0
    */
-  default void properties(final @NotNull Action> configureAction) {
+  default void properties(final Action> configureAction) {
     Configurable.configure(this.getProperties(), configureAction);
   }
 }
diff --git a/src/main/java/net/kyori/blossom/internal/BlossomExtensionImpl.java b/src/main/java/net/kyori/blossom/internal/BlossomExtensionImpl.java
index 376e122..2b89c8b 100644
--- a/src/main/java/net/kyori/blossom/internal/BlossomExtensionImpl.java
+++ b/src/main/java/net/kyori/blossom/internal/BlossomExtensionImpl.java
@@ -28,8 +28,9 @@
 import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer;
 import org.gradle.api.PolymorphicDomainObjectContainer;
 import org.gradle.api.model.ObjectFactory;
-import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
 
+@NullMarked
 public class BlossomExtensionImpl implements BlossomExtension {
   private final ExtensiblePolymorphicDomainObjectContainer templateSets;
 
@@ -41,7 +42,7 @@ public BlossomExtensionImpl(final ObjectFactory objects) {
   }
 
   @Override
-  public @NotNull PolymorphicDomainObjectContainer getTemplateSets() {
+  public PolymorphicDomainObjectContainer getTemplateSets() {
     return this.templateSets;
   }
 }
diff --git a/src/main/java/net/kyori/blossom/internal/FileUtils.java b/src/main/java/net/kyori/blossom/internal/FileUtils.java
index 5851d6f..3dd4d32 100644
--- a/src/main/java/net/kyori/blossom/internal/FileUtils.java
+++ b/src/main/java/net/kyori/blossom/internal/FileUtils.java
@@ -28,21 +28,22 @@
 import java.nio.file.attribute.BasicFileAttributes;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
-import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
 
+@NullMarked
 public final class FileUtils {
   private static final Logger LOGGER = Logging.getLogger(FileUtils.class);
 
   private FileUtils() {
   }
 
-  public static void createDirectoriesSymlinkSafe(final @NotNull Path directory) throws IOException {
+  public static void createDirectoriesSymlinkSafe(final Path directory) throws IOException {
     if (!Files.isDirectory(directory)) { // not checked properly by Files.createDirectories
       Files.createDirectories(directory);
     }
   }
 
-  public static void deleteContents(final @NotNull Path directory) throws IOException {
+  public static void deleteContents(final Path directory) throws IOException {
     if (!Files.isDirectory(directory)) return;
 
     Files.walkFileTree(directory, new FileVisitor<>() {
diff --git a/src/main/java/net/kyori/blossom/internal/IdeConfigurer.java b/src/main/java/net/kyori/blossom/internal/IdeConfigurer.java
index 01fa3ed..b3b2a70 100644
--- a/src/main/java/net/kyori/blossom/internal/IdeConfigurer.java
+++ b/src/main/java/net/kyori/blossom/internal/IdeConfigurer.java
@@ -25,13 +25,14 @@
 import org.gradle.plugins.ide.eclipse.EclipsePlugin;
 import org.gradle.plugins.ide.eclipse.model.EclipseModel;
 import org.gradle.plugins.ide.idea.model.IdeaModel;
-import org.jetbrains.annotations.NotNull;
 import org.jetbrains.gradle.ext.IdeaExtPlugin;
 import org.jetbrains.gradle.ext.ProjectSettings;
+import org.jspecify.annotations.NullMarked;
 
 /**
  * Configures different IDEs when applicable.
  */
+@NullMarked
 public final class IdeConfigurer {
   private static final String IDEA_PLUGIN = "org.jetbrains.gradle.plugin.idea-ext";
 
@@ -67,7 +68,7 @@ public static boolean isEclipseImport() {
    * @param project project to apply to
    * @param toPerform the actions to perform
    */
-  public static void apply(final @NotNull Project project, final @NotNull IdeImportAction toPerform) {
+  public static void apply(final Project project, final IdeImportAction toPerform) {
     project.getPlugins().withId(IDEA_PLUGIN, plugin -> {
       if (!IdeConfigurer.isIdeaImport()) {
         return;
@@ -108,7 +109,7 @@ public interface IdeImportAction {
      * @param idea the basic idea gradle extension
      * @param ideaExtension JetBrain's extensions to the base idea model
      */
-    void idea(final @NotNull Project project, final @NotNull IdeaModel idea, final @NotNull ProjectSettings ideaExtension);
+    void idea(final Project project, final IdeaModel idea, final ProjectSettings ideaExtension);
 
     /**
      * Configure an eclipse project.
@@ -116,6 +117,6 @@ public interface IdeImportAction {
      * @param project the project being imported
      * @param eclipse the eclipse project model to modify
      */
-    void eclipse(final @NotNull Project project, final @NotNull EclipseModel eclipse);
+    void eclipse(final Project project, final EclipseModel eclipse);
   }
 }
diff --git a/src/main/java/net/kyori/blossom/internal/ResourceTemplateSetImpl.java b/src/main/java/net/kyori/blossom/internal/ResourceTemplateSetImpl.java
index 17f331d..ad8c07a 100644
--- a/src/main/java/net/kyori/blossom/internal/ResourceTemplateSetImpl.java
+++ b/src/main/java/net/kyori/blossom/internal/ResourceTemplateSetImpl.java
@@ -27,10 +27,12 @@
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.tasks.SourceSet;
 import org.gradle.api.tasks.TaskProvider;
+import org.jspecify.annotations.NullMarked;
 
 /**
  * A template set that is configured to attach its generated output as a resource directory.
  */
+@NullMarked
 public abstract class ResourceTemplateSetImpl extends TemplateSetImpl implements ResourceTemplateSet {
   @Inject
   public ResourceTemplateSetImpl(final ObjectFactory objects, final String name) {
diff --git a/src/main/java/net/kyori/blossom/internal/SourceTemplateSetImpl.java b/src/main/java/net/kyori/blossom/internal/SourceTemplateSetImpl.java
index a128726..0b78995 100644
--- a/src/main/java/net/kyori/blossom/internal/SourceTemplateSetImpl.java
+++ b/src/main/java/net/kyori/blossom/internal/SourceTemplateSetImpl.java
@@ -30,11 +30,13 @@
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.tasks.SourceSet;
 import org.gradle.api.tasks.TaskProvider;
-import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
 
+@NullMarked
 public abstract class SourceTemplateSetImpl extends TemplateSetImpl implements SourceTemplateSet {
-  private transient SourceSet pendingDestination;
-  private transient TaskProvider pendingGenerateTask;
+  private transient @Nullable SourceSet pendingDestination;
+  private transient @Nullable TaskProvider pendingGenerateTask;
 
   @Inject
   public SourceTemplateSetImpl(final ObjectFactory objects, final String name) {
@@ -80,7 +82,7 @@ public void java() {
   }
 
   @Override
-  public void namedLanguageExtension(final @NotNull String name) {
+  public void namedLanguageExtension(final String name) {
     this.applySourceLens(lensForNamedExtension(name));
   }
 }
diff --git a/src/main/java/net/kyori/blossom/internal/TemplateSetImpl.java b/src/main/java/net/kyori/blossom/internal/TemplateSetImpl.java
index 1fb04c2..8b95b06 100644
--- a/src/main/java/net/kyori/blossom/internal/TemplateSetImpl.java
+++ b/src/main/java/net/kyori/blossom/internal/TemplateSetImpl.java
@@ -28,11 +28,12 @@
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.provider.MapProperty;
 import org.gradle.api.provider.Property;
-import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
 
 /**
  * A directory of templates.
  */
+@NullMarked
 public abstract class TemplateSetImpl implements TemplateSetInternal {
   // shared
   private final ConfigurableFileCollection dataFiles;
@@ -57,46 +58,46 @@ public TemplateSetImpl(final ObjectFactory objects, final String name) {
   }
 
   @Override
-  public @NotNull String getName() {
+  public String getName() {
     return this.name;
   }
 
   // global properties
 
   @Override
-  public @NotNull ConfigurableFileCollection getPropertyFiles() { // if there are variants, reads per-variant data from a list under `variants`
+  public ConfigurableFileCollection getPropertyFiles() { // if there are variants, reads per-variant data from a list under `variants`
     return this.dataFiles;
   }
 
   @Override
-  public @NotNull MapProperty getProperties() {
+  public MapProperty getProperties() {
     return this.properties;
   }
 
   @Override
-  public @NotNull Property getHeader() {
+  public Property getHeader() {
     return this.header;
   }
 
   @Override
-  public @NotNull Property getTrimNewlines() {
+  public Property getTrimNewlines() {
     return this.trimNewlines;
   }
 
   @Override
-  public @NotNull SourceDirectorySet getIncludes() {
+  public SourceDirectorySet getIncludes() {
     return this.includes;
   }
 
   @Override
-  public @NotNull SourceDirectorySet getTemplates() {
+  public SourceDirectorySet getTemplates() {
     return this.templates;
   }
 
   // variant
 
   @Override
-  public @NotNull NamedDomainObjectContainer getVariants() {
+  public NamedDomainObjectContainer getVariants() {
     return this.variants;
   }
 }
diff --git a/src/main/java/net/kyori/blossom/internal/TemplateSetInternal.java b/src/main/java/net/kyori/blossom/internal/TemplateSetInternal.java
index 5597b2d..4e7f3da 100644
--- a/src/main/java/net/kyori/blossom/internal/TemplateSetInternal.java
+++ b/src/main/java/net/kyori/blossom/internal/TemplateSetInternal.java
@@ -25,10 +25,12 @@
 import org.gradle.api.file.Directory;
 import org.gradle.api.tasks.SourceSet;
 import org.gradle.api.tasks.TaskProvider;
+import org.jspecify.annotations.NullMarked;
 
 /**
  * Internal hooks for template sets.
  */
+@NullMarked
 public interface TemplateSetInternal extends TemplateSet {
   // resolve an output directory for templates generated from this set, given build/generated/ as a base
   Directory resolveOutputRoot(final Directory generatedDir);
diff --git a/src/main/java/net/kyori/blossom/internal/VariantImpl.java b/src/main/java/net/kyori/blossom/internal/VariantImpl.java
index 7082fe6..a33a110 100644
--- a/src/main/java/net/kyori/blossom/internal/VariantImpl.java
+++ b/src/main/java/net/kyori/blossom/internal/VariantImpl.java
@@ -27,8 +27,9 @@
 import org.gradle.api.provider.MapProperty;
 import org.gradle.api.tasks.Input;
 import org.gradle.api.tasks.InputFiles;
-import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
 
+@NullMarked
 public class VariantImpl implements Variant {
   private final String name;
   private final ConfigurableFileCollection sourceFiles;
@@ -42,19 +43,19 @@ public VariantImpl(final String name, final ObjectFactory objects) {
   }
 
   @Override
-  public @NotNull String getName() {
+  public String getName() {
     return this.name;
   }
 
   @Override
   @InputFiles
-  public @NotNull ConfigurableFileCollection getPropertyFiles() {
+  public ConfigurableFileCollection getPropertyFiles() {
     return this.sourceFiles;
   }
 
   @Override
   @Input
-  public @NotNull MapProperty getProperties() {
+  public MapProperty getProperties() {
     return this.runtimeProperties;
   }
 }
diff --git a/src/main/java/net/kyori/blossom/internal/worker/GenerateWorker.java b/src/main/java/net/kyori/blossom/internal/worker/GenerateWorker.java
index ec542ee..bd9a7ba 100644
--- a/src/main/java/net/kyori/blossom/internal/worker/GenerateWorker.java
+++ b/src/main/java/net/kyori/blossom/internal/worker/GenerateWorker.java
@@ -37,7 +37,9 @@
 import org.gradle.api.provider.Property;
 import org.gradle.workers.WorkAction;
 import org.gradle.workers.WorkParameters;
+import org.jspecify.annotations.NullMarked;
 
+@NullMarked
 public abstract class GenerateWorker implements WorkAction {
   public interface Params extends WorkParameters {
     // parameters + data files
diff --git a/src/test/java/net/kyori/blossom/test/BlossomDisplayNameGeneration.java b/src/test/java/net/kyori/blossom/test/BlossomDisplayNameGeneration.java
index 83193a6..900ae46 100644
--- a/src/test/java/net/kyori/blossom/test/BlossomDisplayNameGeneration.java
+++ b/src/test/java/net/kyori/blossom/test/BlossomDisplayNameGeneration.java
@@ -22,8 +22,10 @@
 
 import java.lang.reflect.Method;
 import java.util.List;
+import org.jspecify.annotations.NullMarked;
 import org.junit.jupiter.api.DisplayNameGenerator;
 
+@NullMarked
 public class BlossomDisplayNameGeneration extends DisplayNameGenerator.Standard {
   @Override
   public String generateDisplayNameForMethod(final List> enclosingTypes, final Class> clazz, final Method testMethod) {
diff --git a/src/test/java/net/kyori/blossom/test/SettingsFactory.java b/src/test/java/net/kyori/blossom/test/SettingsFactory.java
index 73e4590..01bc811 100644
--- a/src/test/java/net/kyori/blossom/test/SettingsFactory.java
+++ b/src/test/java/net/kyori/blossom/test/SettingsFactory.java
@@ -22,7 +22,9 @@
 
 import java.io.IOException;
 import net.kyori.mammoth.test.TestContext;
+import org.jspecify.annotations.NullMarked;
 
+@NullMarked
 public final class SettingsFactory {
   private SettingsFactory() {
   }
diff --git a/src/workerClasspath/java/net/kyori/blossom/internal/worker/GenerateWorkerInvokerImpl.java b/src/workerClasspath/java/net/kyori/blossom/internal/worker/GenerateWorkerInvokerImpl.java
index 148fb45..e054c2c 100644
--- a/src/workerClasspath/java/net/kyori/blossom/internal/worker/GenerateWorkerInvokerImpl.java
+++ b/src/workerClasspath/java/net/kyori/blossom/internal/worker/GenerateWorkerInvokerImpl.java
@@ -40,8 +40,10 @@
 import java.util.stream.Stream;
 import org.gradle.api.GradleException;
 import org.gradle.api.InvalidUserDataException;
-import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
 
+@NullMarked
 public class GenerateWorkerInvokerImpl implements GenerateWorkerInvoker {
   private static final String FILE_NAME_CACHE_DISAMBIGUATOR = "###";
   private static final String PEBBLE_EXTENSION = ".peb";
diff --git a/src/workerClasspath/java/net/kyori/blossom/internal/worker/MultiDirectoryLoader.java b/src/workerClasspath/java/net/kyori/blossom/internal/worker/MultiDirectoryLoader.java
index ea096f2..13b6248 100644
--- a/src/workerClasspath/java/net/kyori/blossom/internal/worker/MultiDirectoryLoader.java
+++ b/src/workerClasspath/java/net/kyori/blossom/internal/worker/MultiDirectoryLoader.java
@@ -31,8 +31,10 @@
 import java.nio.file.Path;
 import java.util.List;
 import java.util.stream.Collectors;
-import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
 
+@NullMarked
 final class MultiDirectoryLoader implements Loader {
   private final List directories;
   private final Charset charset;
@@ -57,7 +59,7 @@ public Reader getReader(final String templateName) {
 
   private @Nullable Path findFile(final String templateName) {
     for (final Path path : this.directories) {
-      @Nullable Path file = findFileIn(templateName, path);
+      Path file = findFileIn(templateName, path);
 
       if (file == null && !templateName.endsWith(".peb")) {
         file = findFileIn(templateName + ".peb", path);
diff --git a/src/workerClasspath/java/net/kyori/blossom/internal/worker/PropertyFileIO.java b/src/workerClasspath/java/net/kyori/blossom/internal/worker/PropertyFileIO.java
index 65dfd33..b4752e3 100644
--- a/src/workerClasspath/java/net/kyori/blossom/internal/worker/PropertyFileIO.java
+++ b/src/workerClasspath/java/net/kyori/blossom/internal/worker/PropertyFileIO.java
@@ -33,10 +33,13 @@
 import java.util.Set;
 import org.gradle.api.GradleException;
 import org.gradle.api.InvalidUserDataException;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
 import org.snakeyaml.engine.v2.api.Load;
 import org.snakeyaml.engine.v2.api.LoadSettings;
 import org.snakeyaml.engine.v2.exceptions.YamlEngineException;
 
+@NullMarked
 final class PropertyFileIO {
 
   private PropertyFileIO() {
@@ -124,7 +127,7 @@ private static Map> loadConfig(final String template
     return templateParams;
   }
 
-  private static void unmarshalData(final Map> output, final Object data, final boolean useVariants) {
+  private static void unmarshalData(final Map<@Nullable String, Map<@Nullable String, Object>> output, final Object data, final boolean useVariants) {
     if (!(data instanceof Map, ?>)) {
       throw new InvalidUserDataException("Template data files must have a mapping as the root node");
     }
@@ -149,8 +152,8 @@ private static void unmarshalData(final Map> output,
     output.put(null, makeStringKeys((Map, ?>) data));
   }
 
-  private static Map makeStringKeys(final Map, ?> map) {
-    final Map ret = new LinkedHashMap<>();
+  private static Map<@Nullable String, Object> makeStringKeys(final Map extends @Nullable Object, ?> map) {
+    final Map<@Nullable String, Object> ret = new LinkedHashMap<>();
     for (final Map.Entry, ?> entry : map.entrySet()) {
       ret.put(entry.getKey() == null ? null : entry.getKey().toString(), entry.getValue());
     }