diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,14 @@
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 0.8.1.0 - 2023-06-28
+
+### Added
+
+ - `getJob` (by id)
+ - `projectJobEvaluationDiff` for diffing a pair of evaluations
+ - `Job.{repo,owner,forge}Name`
+
 ## 0.8.0.0 - 2023-03-06
 
 ### Changed
diff --git a/hercules-ci-api.cabal b/hercules-ci-api.cabal
--- a/hercules-ci-api.cabal
+++ b/hercules-ci-api.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.2
 name:               hercules-ci-api
-version:            0.8.0.0
+version:            0.8.1.0
 synopsis:           Hercules CI API definition with Servant
 homepage:           https://github.com/hercules-ci/hercules-ci-agent#readme
 bug-reports:        https://github.com/hercules-ci/hercules-ci-agent/issues
@@ -21,8 +21,10 @@
     NoImplicitPrelude
     DeriveGeneric
     DeriveTraversable
+    DerivingStrategies
     DisambiguateRecordFields
     FlexibleContexts
+    ImportQualifiedPost
     InstanceSigs
     LambdaCase
     MultiParamTypeClasses
@@ -75,6 +77,7 @@
     Hercules.API.Build.DerivationInfo.DerivationOutput
     Hercules.API.Build.EvaluationDependency
     Hercules.API.Build.EvaluationDetail
+    Hercules.API.Build.EvaluationDiff
     Hercules.API.Build.FailureGraph
     Hercules.API.Build.Log
     Hercules.API.Build.LogLine
@@ -93,7 +96,6 @@
     Hercules.API.Forge.SimpleForge
     Hercules.API.GitLab
     Hercules.API.GitLab.CreateInstallationBuilderRequest
-    Hercules.API.GitLab.CreateInstallationBuilderResponse
     Hercules.API.GitLab.InstallationBuilder
     Hercules.API.GitLab.PatchInstallationBuilder
     Hercules.API.Health
@@ -126,6 +128,7 @@
     Hercules.API.Repos.SimpleRepo
     Hercules.API.Result
     Hercules.API.Servant.Status
+    Hercules.API.SimpleAttribute
     Hercules.API.State
     Hercules.API.State.ProjectState
     Hercules.API.State.StateFile
diff --git a/src/Hercules/API/Accounts/Account.hs b/src/Hercules/API/Accounts/Account.hs
--- a/src/Hercules/API/Accounts/Account.hs
+++ b/src/Hercules/API/Accounts/Account.hs
@@ -1,16 +1,19 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Accounts.Account where
 
 import Hercules.API.Forge.Forge (Forge)
-import qualified Hercules.API.Organizations.Organization as Organization
+import Hercules.API.Organizations.Organization qualified as Organization
 import Hercules.API.Prelude
 
 data AccountType = User | Organization
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data MembershipRole = Member | Admin
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data Account = Account
   { id :: Id Account,
@@ -39,4 +42,5 @@
     manageInstallationURL :: Maybe Text,
     installationIsSelection :: Maybe Bool
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Accounts/AccountInstallationStatus.hs b/src/Hercules/API/Accounts/AccountInstallationStatus.hs
--- a/src/Hercules/API/Accounts/AccountInstallationStatus.hs
+++ b/src/Hercules/API/Accounts/AccountInstallationStatus.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Accounts.AccountInstallationStatus where
 
@@ -12,4 +13,5 @@
     isProcessingInstallationWebHook :: Bool,
     secondsSinceInstallationWebHookComplete :: Maybe Int
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Accounts/AccountSettings.hs b/src/Hercules/API/Accounts/AccountSettings.hs
--- a/src/Hercules/API/Accounts/AccountSettings.hs
+++ b/src/Hercules/API/Accounts/AccountSettings.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Accounts.AccountSettings where
 
@@ -8,4 +9,5 @@
   { -- | When a new repo is created/installed, enable building it?
     enableNewRepos :: Bool
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Accounts/AccountSettingsPatch.hs b/src/Hercules/API/Accounts/AccountSettingsPatch.hs
--- a/src/Hercules/API/Accounts/AccountSettingsPatch.hs
+++ b/src/Hercules/API/Accounts/AccountSettingsPatch.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Accounts.AccountSettingsPatch where
 
@@ -7,4 +8,5 @@
 data AccountSettingsPatch = AccountSettingsPatch
   { enableNewRepos :: Maybe Bool
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Accounts/CLIAuthorizationRequest.hs b/src/Hercules/API/Accounts/CLIAuthorizationRequest.hs
--- a/src/Hercules/API/Accounts/CLIAuthorizationRequest.hs
+++ b/src/Hercules/API/Accounts/CLIAuthorizationRequest.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Accounts.CLIAuthorizationRequest where
 
@@ -8,4 +9,5 @@
   { description :: Text,
     creationTime :: UTCTime
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Accounts/CLIAuthorizationRequestCreate.hs b/src/Hercules/API/Accounts/CLIAuthorizationRequestCreate.hs
--- a/src/Hercules/API/Accounts/CLIAuthorizationRequestCreate.hs
+++ b/src/Hercules/API/Accounts/CLIAuthorizationRequestCreate.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Accounts.CLIAuthorizationRequestCreate where
 
@@ -7,4 +8,5 @@
 data CLIAuthorizationRequestCreate = CLIAuthorizationRequestCreate
   { description :: Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Accounts/CLIAuthorizationRequestCreateResponse.hs b/src/Hercules/API/Accounts/CLIAuthorizationRequestCreateResponse.hs
--- a/src/Hercules/API/Accounts/CLIAuthorizationRequestCreateResponse.hs
+++ b/src/Hercules/API/Accounts/CLIAuthorizationRequestCreateResponse.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Accounts.CLIAuthorizationRequestCreateResponse where
 
@@ -8,4 +9,5 @@
   { temporaryCLIToken :: Text,
     browserURL :: Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Accounts/CLIAuthorizationRequestStatus.hs b/src/Hercules/API/Accounts/CLIAuthorizationRequestStatus.hs
--- a/src/Hercules/API/Accounts/CLIAuthorizationRequestStatus.hs
+++ b/src/Hercules/API/Accounts/CLIAuthorizationRequestStatus.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Accounts.CLIAuthorizationRequestStatus where
 
@@ -8,12 +9,15 @@
   { token :: Text,
     userIdentities :: [Text]
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data CLIAuthorizationStatus = Pending () | Granted CLIAuthorization
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data CLIAuthorizationRequestStatus = CLIAuthorizationRequestStatus
   { status :: CLIAuthorizationStatus
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Accounts/CLIToken.hs b/src/Hercules/API/Accounts/CLIToken.hs
--- a/src/Hercules/API/Accounts/CLIToken.hs
+++ b/src/Hercules/API/Accounts/CLIToken.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Accounts.CLIToken where
 
@@ -12,4 +13,5 @@
     creationTime :: UTCTime,
     userId :: Id Account
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Accounts/CLITokensResponse.hs b/src/Hercules/API/Accounts/CLITokensResponse.hs
--- a/src/Hercules/API/Accounts/CLITokensResponse.hs
+++ b/src/Hercules/API/Accounts/CLITokensResponse.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Accounts.CLITokensResponse where
 
@@ -8,4 +9,5 @@
 data CLITokensResponse = CLITokensResponse
   { cliTokens :: [CLIToken]
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Accounts/NotificationSettings.hs b/src/Hercules/API/Accounts/NotificationSettings.hs
--- a/src/Hercules/API/Accounts/NotificationSettings.hs
+++ b/src/Hercules/API/Accounts/NotificationSettings.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Accounts.NotificationSettings where
 
@@ -10,30 +11,35 @@
 data NotificationLevel
   = Ignore
   | All
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data NotificationSetting = NotificationSetting
   { notificationLevel :: Maybe NotificationLevel,
     notificationEmail :: Maybe Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data NotificationAccountOverride = NotificationSettingsOverride
   { account :: SimpleAccount,
     setting :: NotificationSetting
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data AuthorizedEmail = AuthorizedEmail
   { address :: Text,
     isPrimary :: Bool,
     source :: Maybe SimpleForge
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data NotificationSettings = NotificationSettings
   { authorizedEmails :: [AuthorizedEmail],
     defaultSetting :: Maybe NotificationSetting,
     accountOverrides :: [NotificationAccountOverride]
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Accounts/NotificationSettingsPatch.hs b/src/Hercules/API/Accounts/NotificationSettingsPatch.hs
--- a/src/Hercules/API/Accounts/NotificationSettingsPatch.hs
+++ b/src/Hercules/API/Accounts/NotificationSettingsPatch.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Accounts.NotificationSettingsPatch where
 
@@ -10,4 +11,5 @@
   { defaultSetting :: Maybe NotificationSetting,
     accountOverrides :: Map (Id Account) NotificationSetting
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Accounts/SimpleAccount.hs b/src/Hercules/API/Accounts/SimpleAccount.hs
--- a/src/Hercules/API/Accounts/SimpleAccount.hs
+++ b/src/Hercules/API/Accounts/SimpleAccount.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Accounts.SimpleAccount where
 
@@ -14,4 +15,5 @@
     imageURL :: Text,
     site :: SimpleForge
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Agents/AgentInfo.hs b/src/Hercules/API/Agents/AgentInfo.hs
--- a/src/Hercules/API/Agents/AgentInfo.hs
+++ b/src/Hercules/API/Agents/AgentInfo.hs
@@ -4,7 +4,7 @@
 
 import Control.Applicative
 import Control.Lens (at, (%~))
-import qualified Data.Aeson as A
+import Data.Aeson qualified as A
 import Data.Aeson.Lens (_Object)
 import Hercules.API.Prelude
 
diff --git a/src/Hercules/API/Agents/AgentSession.hs b/src/Hercules/API/Agents/AgentSession.hs
--- a/src/Hercules/API/Agents/AgentSession.hs
+++ b/src/Hercules/API/Agents/AgentSession.hs
@@ -23,8 +23,10 @@
     substituters :: [Text],
     creation :: UTCTime,
     lastSeen :: Maybe UTCTime,
+    revocation :: Maybe UTCTime,
     available :: Bool,
     concurrentTasks :: Int,
     labels :: Labels
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Agents/ClusterJoinToken.hs b/src/Hercules/API/Agents/ClusterJoinToken.hs
--- a/src/Hercules/API/Agents/ClusterJoinToken.hs
+++ b/src/Hercules/API/Agents/ClusterJoinToken.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Agents.ClusterJoinToken where
 
@@ -12,4 +13,5 @@
     -- TODO lastAccessTime :: Maybe UTCTime
     description :: Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Agents/CreateClusterJoinToken.hs b/src/Hercules/API/Agents/CreateClusterJoinToken.hs
--- a/src/Hercules/API/Agents/CreateClusterJoinToken.hs
+++ b/src/Hercules/API/Agents/CreateClusterJoinToken.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Agents.CreateClusterJoinToken where
 
@@ -8,4 +9,5 @@
 data CreateClusterJoinToken = CreateClusterJoinToken
   { description :: Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Agents/FullClusterJoinToken.hs b/src/Hercules/API/Agents/FullClusterJoinToken.hs
--- a/src/Hercules/API/Agents/FullClusterJoinToken.hs
+++ b/src/Hercules/API/Agents/FullClusterJoinToken.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Agents.FullClusterJoinToken where
 
@@ -11,4 +12,5 @@
   { metadata :: ClusterJoinToken,
     token :: Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Attribute.hs b/src/Hercules/API/Attribute.hs
--- a/src/Hercules/API/Attribute.hs
+++ b/src/Hercules/API/Attribute.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 {-# LANGUAGE StandaloneDeriving #-}
 
 module Hercules.API.Attribute
@@ -12,12 +13,12 @@
 
 import Control.Applicative (Alternative ((<|>)))
 import Control.Lens (at, (%~))
-import qualified Data.Aeson as A
+import Data.Aeson qualified as A
 import Data.Aeson.Lens
 import Data.Function ((&))
 import Data.Proxy (Proxy (Proxy))
 import Data.Swagger (ToParamSchema (..))
-import qualified Data.Text as T
+import Data.Text qualified as T
 import Hercules.API.Prelude
 import Servant.API (FromHttpApiData (..), ToHttpApiData (..))
 import Prelude ()
@@ -28,7 +29,8 @@
   | MayFail
   | DependenciesOnly
   | Effect
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 -- | An arbitrary ordering
 deriving instance Ord AttributeType
@@ -38,7 +40,8 @@
     value :: a,
     typ :: AttributeType
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON)
 
 instance FromJSON a => FromJSON (Attribute a) where
   parseJSON v = A.parseJSON (fixup v)
@@ -55,7 +58,8 @@
 deriving instance Traversable Attribute
 
 newtype AttributePath = AttributePath {fromAttributePath :: [Text]}
-  deriving (Generic, Eq, NFData)
+  deriving (Generic, Eq)
+  deriving anyclass (NFData)
 
 instance FromHttpApiData AttributePath where
   parseUrlPiece = Right . AttributePath . attributePathFromString
diff --git a/src/Hercules/API/BillingStatus.hs b/src/Hercules/API/BillingStatus.hs
--- a/src/Hercules/API/BillingStatus.hs
+++ b/src/Hercules/API/BillingStatus.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.BillingStatus
   ( BillingStatus (..),
@@ -17,7 +18,8 @@
   | Cancelled
   | External
   | Enterprise
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 toText :: BillingStatus -> Text
 toText Community = "Community"
diff --git a/src/Hercules/API/Build/AgentRequirements.hs b/src/Hercules/API/Build/AgentRequirements.hs
--- a/src/Hercules/API/Build/AgentRequirements.hs
+++ b/src/Hercules/API/Build/AgentRequirements.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Build.AgentRequirements where
 
@@ -8,4 +9,5 @@
   { platform :: Text,
     requiredSystemFeatures :: [Text]
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Build/DerivationEvent.hs b/src/Hercules/API/Build/DerivationEvent.hs
--- a/src/Hercules/API/Build/DerivationEvent.hs
+++ b/src/Hercules/API/Build/DerivationEvent.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 {-# LANGUAGE DuplicateRecordFields #-}
 
 module Hercules.API.Build.DerivationEvent where
@@ -50,12 +51,14 @@
     requeuedForEvalOfJob :: Maybe SimpleJob,
     requeuedForAgent :: Maybe Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data DerivationEventDependencyFailed = DerivationEventDependencyFailed
   { time :: UTCTime
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data DerivationEventStarted = DerivationEventStarted
   { time :: UTCTime,
@@ -63,47 +66,56 @@
     agentHostname :: Maybe Text,
     streamable :: Bool
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data DerivationEventReset = DerivationEventReset
   { time :: UTCTime
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data DerivationEventFailed = DerivationEventFailed
   { time :: UTCTime,
     technicalError :: Maybe Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data DerivationEventSucceeded = DerivationEventSucceeded
   { time :: UTCTime
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data DerivationEventCancelled = DerivationEventCancelled
   { time :: UTCTime
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data DerivationEventForceCancelled = DerivationEventForceCancelled
   { time :: UTCTime,
     byUser :: Maybe SimpleAccount
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data DerivationEventBuilt = DerivationEventBuilt
   { time :: UTCTime,
     outputs :: [BuiltOutput]
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data DerivationEventHasCancelledForReset = DerivationEventHasCancelledForReset
   { time :: UTCTime
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data DerivationEventHasCancelled = DerivationEventHasCancelled
   { time :: UTCTime
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Build/DerivationEvent/BuiltOutput.hs b/src/Hercules/API/Build/DerivationEvent/BuiltOutput.hs
--- a/src/Hercules/API/Build/DerivationEvent/BuiltOutput.hs
+++ b/src/Hercules/API/Build/DerivationEvent/BuiltOutput.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Build.DerivationEvent.BuiltOutput where
 
@@ -10,4 +11,5 @@
     hash :: Text,
     size :: Int64
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Build/DerivationInfo.hs b/src/Hercules/API/Build/DerivationInfo.hs
--- a/src/Hercules/API/Build/DerivationInfo.hs
+++ b/src/Hercules/API/Build/DerivationInfo.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Build.DerivationInfo where
 
@@ -24,4 +25,5 @@
     mayCancel :: Bool,
     dummy :: Maybe DerivationEvent -- TODO: remove and update/fix codegen
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Build/DerivationInfo/DerivationInput.hs b/src/Hercules/API/Build/DerivationInfo/DerivationInput.hs
--- a/src/Hercules/API/Build/DerivationInfo/DerivationInput.hs
+++ b/src/Hercules/API/Build/DerivationInfo/DerivationInput.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Build.DerivationInfo.DerivationInput where
 
@@ -11,4 +12,5 @@
     outputName :: Text,
     outputPath :: Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Build/DerivationInfo/DerivationOutput.hs b/src/Hercules/API/Build/DerivationInfo/DerivationOutput.hs
--- a/src/Hercules/API/Build/DerivationInfo/DerivationOutput.hs
+++ b/src/Hercules/API/Build/DerivationInfo/DerivationOutput.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Build.DerivationInfo.DerivationOutput where
 
@@ -8,4 +9,5 @@
   { outputName :: Text,
     outputPath :: Maybe Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Build/EvaluationDependency.hs b/src/Hercules/API/Build/EvaluationDependency.hs
--- a/src/Hercules/API/Build/EvaluationDependency.hs
+++ b/src/Hercules/API/Build/EvaluationDependency.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Build.EvaluationDependency where
 
@@ -10,4 +11,5 @@
     derivation :: Derivation,
     outputName :: Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Build/EvaluationDetail.hs b/src/Hercules/API/Build/EvaluationDetail.hs
--- a/src/Hercules/API/Build/EvaluationDetail.hs
+++ b/src/Hercules/API/Build/EvaluationDetail.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Build.EvaluationDetail where
 
@@ -38,7 +39,9 @@
     derivationsCancelledCount :: Int,
     unmetAgentRequirements :: [AgentRequirements]
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 newtype IFDAttribute = IFDAttribute (Attribute Derivation)
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Build/EvaluationDiff.hs b/src/Hercules/API/Build/EvaluationDiff.hs
new file mode 100644
--- /dev/null
+++ b/src/Hercules/API/Build/EvaluationDiff.hs
@@ -0,0 +1,63 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE StandaloneDeriving #-}
+
+module Hercules.API.Build.EvaluationDiff
+  ( EvaluationDiff (..),
+    AttributeDiff (..),
+    AttributeValueDiff (..),
+    Diff (..),
+    IFDDiff (..),
+    DerivationOutputNamePair (..),
+  )
+where
+
+import Hercules.API.Attribute (Attribute)
+import Hercules.API.Derivation (Derivation)
+import Hercules.API.Evaluation.AttributeError (AttributeError)
+import Hercules.API.Evaluation.Evaluation (Evaluation)
+import Hercules.API.Prelude
+import Hercules.API.Result (Result)
+import Hercules.API.SimpleAttribute (SimpleAttribute)
+
+-- | Generic type for additions, remvals and changes. Addition and removal are
+-- represented by nulling the appropriate field.
+--
+-- This gives the best JSON representation, despite the fact that "Absence" is
+-- representable: @{before: null, after: null}@. Most - if not all - endpoints
+-- can be expected to not return such a value.
+--
+-- NOTE: Generic types must always be wrapped in a newtype, so as to avoid
+--       ambiguities in the generated schema.
+data Diff a = Diff {before :: Maybe a, after :: Maybe a}
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON)
+
+deriving instance ToSchema a => ToSchema (Diff a)
+
+newtype AttributeDiff = AttributeDiff (SimpleAttribute AttributeValueDiff)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
+
+newtype AttributeValueDiff = AttributeValueDiff (Diff (Attribute (Result AttributeError Derivation)))
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
+
+newtype IFDDiff = IFDDiff (Diff DerivationOutputNamePair)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
+
+data DerivationOutputNamePair = DerivationOutputNamePair
+  { derivation :: Derivation,
+    outputName :: Text
+  }
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
+
+data EvaluationDiff = EvaluationDiff
+  { beforeId :: Id Evaluation,
+    afterId :: Id Evaluation,
+    attributes :: [AttributeDiff],
+    ifds :: [IFDDiff]
+  }
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Build/FailureGraph.hs b/src/Hercules/API/Build/FailureGraph.hs
--- a/src/Hercules/API/Build/FailureGraph.hs
+++ b/src/Hercules/API/Build/FailureGraph.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Build.FailureGraph where
 
@@ -11,7 +12,8 @@
 data Graph = Graph
   { nodes :: [Node]
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 -- | A derivation and any dependencies that caused it to fail, if applicable.
 data Node = Node
@@ -20,4 +22,5 @@
     --   DependencyFailure for this derivation.
     failedDependencies :: [Text]
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Build/Log.hs b/src/Hercules/API/Build/Log.hs
--- a/src/Hercules/API/Build/Log.hs
+++ b/src/Hercules/API/Build/Log.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Build.Log where
 
@@ -11,4 +12,5 @@
     lines :: [LogLine],
     done :: Bool
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Build/LogLine.hs b/src/Hercules/API/Build/LogLine.hs
--- a/src/Hercules/API/Build/LogLine.hs
+++ b/src/Hercules/API/Build/LogLine.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Build.LogLine where
 
@@ -6,4 +7,5 @@
 import Hercules.API.Prelude
 
 data LogLine = LogLine {i :: !Word64, ms :: !Word64, t :: !Text}
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/ClientInfo/ClientInfo.hs b/src/Hercules/API/ClientInfo/ClientInfo.hs
--- a/src/Hercules/API/ClientInfo/ClientInfo.hs
+++ b/src/Hercules/API/ClientInfo/ClientInfo.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.ClientInfo.ClientInfo where
 
@@ -14,4 +15,5 @@
     -- | @[]@ for unauthenticated users.
     personalAccounts :: [Account]
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Derivation.hs b/src/Hercules/API/Derivation.hs
--- a/src/Hercules/API/Derivation.hs
+++ b/src/Hercules/API/Derivation.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Derivation where
 
@@ -7,13 +8,15 @@
 data DerivationPath = DerivationPath
   { drvPath :: Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data Derivation = Derivation
   { status :: DerivationStatus,
     derivationPath :: Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data DerivationStatus
   = Waiting
@@ -22,4 +25,5 @@
   | DependencyFailure
   | BuildSuccess
   | Cancelled
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Effects/EffectEvent.hs b/src/Hercules/API/Effects/EffectEvent.hs
--- a/src/Hercules/API/Effects/EffectEvent.hs
+++ b/src/Hercules/API/Effects/EffectEvent.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 {-# LANGUAGE DuplicateRecordFields #-}
 
 module Hercules.API.Effects.EffectEvent where
@@ -35,12 +36,14 @@
 data EffectEventQueued = EffectEventQueued
   { time :: UTCTime
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data EffectEventDependencyFailed = EffectEventDependencyFailed
   { time :: UTCTime
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data EffectEventStarted = EffectEventStarted
   { time :: UTCTime,
@@ -48,25 +51,30 @@
     agentHostname :: Text,
     agentVersion :: Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data EffectEventReset = EffectEventReset
   { time :: UTCTime
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data EffectEventFailed = EffectEventFailed
   { time :: UTCTime,
     technicalError :: Maybe Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data EffectEventSucceeded = EffectEventSucceeded
   { time :: UTCTime
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data EffectEventCancelled = EffectEventCancelled
   { time :: UTCTime
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Effects/EffectInfo.hs b/src/Hercules/API/Effects/EffectInfo.hs
--- a/src/Hercules/API/Effects/EffectInfo.hs
+++ b/src/Hercules/API/Effects/EffectInfo.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Effects.EffectInfo where
 
@@ -17,7 +18,8 @@
   | DependencyFailed
   | Successful
   | Cancelled
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data EffectInfo = EffectInfo
   { status :: EffectStatus,
@@ -32,4 +34,5 @@
     mayCancel :: Bool,
     dummy :: Maybe EffectEvent -- TODO: remove and update/fix codegen
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Effects/EffectReference.hs b/src/Hercules/API/Effects/EffectReference.hs
--- a/src/Hercules/API/Effects/EffectReference.hs
+++ b/src/Hercules/API/Effects/EffectReference.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Effects.EffectReference where
 
@@ -9,4 +10,5 @@
   { job :: SimpleJob,
     attributePath :: [Text]
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Error.hs b/src/Hercules/API/Error.hs
--- a/src/Hercules/API/Error.hs
+++ b/src/Hercules/API/Error.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Error where
 
@@ -13,4 +14,5 @@
     -- | Human-readable error message.
     message :: Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Evaluation/AttributeError.hs b/src/Hercules/API/Evaluation/AttributeError.hs
--- a/src/Hercules/API/Evaluation/AttributeError.hs
+++ b/src/Hercules/API/Evaluation/AttributeError.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Evaluation.AttributeError where
 
@@ -12,4 +13,5 @@
     errorDerivation :: Maybe Text,
     trace :: Maybe Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Evaluation/Evaluation.hs b/src/Hercules/API/Evaluation/Evaluation.hs
--- a/src/Hercules/API/Evaluation/Evaluation.hs
+++ b/src/Hercules/API/Evaluation/Evaluation.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Evaluation.Evaluation where
 
@@ -7,4 +8,5 @@
 data Evaluation = Evaluation
   { id :: Id Evaluation
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Forge/Forge.hs b/src/Hercules/API/Forge/Forge.hs
--- a/src/Hercules/API/Forge/Forge.hs
+++ b/src/Hercules/API/Forge/Forge.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Forge.Forge where
 
@@ -15,4 +16,5 @@
     -- | Does the requesting user have admin permissions?
     adminPermission :: Maybe Bool
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Forge/SimpleForge.hs b/src/Hercules/API/Forge/SimpleForge.hs
--- a/src/Hercules/API/Forge/SimpleForge.hs
+++ b/src/Hercules/API/Forge/SimpleForge.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Forge.SimpleForge where
 
@@ -10,4 +11,5 @@
     name :: Name Forge,
     displayName :: Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/GitLab/CreateInstallationBuilderRequest.hs b/src/Hercules/API/GitLab/CreateInstallationBuilderRequest.hs
--- a/src/Hercules/API/GitLab/CreateInstallationBuilderRequest.hs
+++ b/src/Hercules/API/GitLab/CreateInstallationBuilderRequest.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.GitLab.CreateInstallationBuilderRequest where
 
@@ -9,4 +10,5 @@
     gitlabAdminUsername :: Text,
     gitlabAdminPassword :: Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/GitLab/CreateInstallationBuilderResponse.hs b/src/Hercules/API/GitLab/CreateInstallationBuilderResponse.hs
deleted file mode 100644
--- a/src/Hercules/API/GitLab/CreateInstallationBuilderResponse.hs
+++ /dev/null
@@ -1,12 +0,0 @@
-{-# LANGUAGE DeriveAnyClass #-}
-
-module Hercules.API.GitLab.CreateInstallationBuilderResponse where
-
-import Hercules.API.GitLab.InstallationBuilder (InstallationBuilder)
-import Hercules.API.Prelude
-
-data CreateInstallationBuilderResponse = CreateInstallationBuilderResponse
-  { ok :: Maybe InstallationBuilder,
-    error :: Maybe Text
-  }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/GitLab/InstallationBuilder.hs b/src/Hercules/API/GitLab/InstallationBuilder.hs
--- a/src/Hercules/API/GitLab/InstallationBuilder.hs
+++ b/src/Hercules/API/GitLab/InstallationBuilder.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.GitLab.InstallationBuilder where
 
@@ -14,9 +15,11 @@
     forge :: Maybe SimpleForge,
     errors :: [Error]
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data InstallationBuilders = InstallationBuilders
   { items :: [InstallationBuilder]
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/GitLab/PatchInstallationBuilder.hs b/src/Hercules/API/GitLab/PatchInstallationBuilder.hs
--- a/src/Hercules/API/GitLab/PatchInstallationBuilder.hs
+++ b/src/Hercules/API/GitLab/PatchInstallationBuilder.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.GitLab.PatchInstallationBuilder where
 
@@ -8,4 +9,5 @@
   { name :: Maybe Text,
     displayName :: Maybe Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Health.hs b/src/Hercules/API/Health.hs
--- a/src/Hercules/API/Health.hs
+++ b/src/Hercules/API/Health.hs
@@ -2,8 +2,8 @@
 
 module Hercules.API.Health where
 
-import Hercules.API.Prelude
 import Servant.API
+import Servant.API.Generic
 
 data HealthAPI auth f = HealthAPI
   { db ::
diff --git a/src/Hercules/API/Inputs/ImmutableGitInput.hs b/src/Hercules/API/Inputs/ImmutableGitInput.hs
--- a/src/Hercules/API/Inputs/ImmutableGitInput.hs
+++ b/src/Hercules/API/Inputs/ImmutableGitInput.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Inputs.ImmutableGitInput where
 
@@ -15,4 +16,5 @@
     jobs :: Maybe [SimpleJob],
     historyURL :: Maybe Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Message.hs b/src/Hercules/API/Message.hs
--- a/src/Hercules/API/Message.hs
+++ b/src/Hercules/API/Message.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Message where
 
@@ -9,7 +10,8 @@
     typ :: Type,
     message :: Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data Type
   = -- | Something went wrong, inform user about possible
@@ -24,4 +26,5 @@
     -- @trace@. Indeed side effecting evaluation breaks the
     -- abstraction.
     Trace
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Organizations/BillingInfo.hs b/src/Hercules/API/Organizations/BillingInfo.hs
--- a/src/Hercules/API/Organizations/BillingInfo.hs
+++ b/src/Hercules/API/Organizations/BillingInfo.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Organizations.BillingInfo where
 
@@ -9,4 +10,5 @@
   { nextPayment :: Maybe UTCTime,
     activeUsers :: [Account]
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Organizations/CreateOrganization.hs b/src/Hercules/API/Organizations/CreateOrganization.hs
--- a/src/Hercules/API/Organizations/CreateOrganization.hs
+++ b/src/Hercules/API/Organizations/CreateOrganization.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Organizations.CreateOrganization where
 
@@ -9,4 +10,5 @@
   { displayName :: Text,
     primaryAccountId :: Id Account
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Organizations/Organization.hs b/src/Hercules/API/Organizations/Organization.hs
--- a/src/Hercules/API/Organizations/Organization.hs
+++ b/src/Hercules/API/Organizations/Organization.hs
@@ -1,8 +1,9 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Organizations.Organization where
 
-import qualified Hercules.API.BillingStatus as BillingStatus
+import Hercules.API.BillingStatus qualified as BillingStatus
 import Hercules.API.Prelude
 
 data Organization = Organization
@@ -15,4 +16,5 @@
     subscriptionCancelUrl :: Maybe Text,
     subscriptionUpdateUrl :: Maybe Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Organizations/PaymentLink.hs b/src/Hercules/API/Organizations/PaymentLink.hs
--- a/src/Hercules/API/Organizations/PaymentLink.hs
+++ b/src/Hercules/API/Organizations/PaymentLink.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Organizations.PaymentLink where
 
@@ -8,4 +9,5 @@
   { url :: Text,
     productId :: Integer
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Projects.hs b/src/Hercules/API/Projects.hs
--- a/src/Hercules/API/Projects.hs
+++ b/src/Hercules/API/Projects.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Projects where
 
@@ -7,7 +8,8 @@
 import Hercules.API.Build.EvaluationDetail
   ( EvaluationDetail,
   )
-import qualified Hercules.API.Build.FailureGraph as FailureGraph
+import Hercules.API.Build.EvaluationDiff (EvaluationDiff)
+import Hercules.API.Build.FailureGraph qualified as FailureGraph
 import Hercules.API.Build.Log (Log)
 import Hercules.API.Forge.Forge (Forge)
 import Hercules.API.Inputs.ImmutableGitInput (ImmutableGitInput)
@@ -145,6 +147,14 @@
           :> QueryParam' '[Optional, Description "Number of latest jobs to get, when project name is omitted. Range [1..50], default 10."] "latest" Int
           :> auth
           :> Get '[JSON] [ProjectAndJobs],
+    getJob ::
+      f
+        :- Summary "Retrieve a job"
+          :> Description "Retrieve a job"
+          :> "jobs"
+          :> Capture' '[Required, Strict] "jobId" (Id Job)
+          :> auth
+          :> Get '[JSON] Job,
     getJobHandlers ::
       f
         :- Summary "Get a job's handler declarations, if any."
@@ -163,6 +173,17 @@
           :> "evaluation"
           :> auth
           :> GetJsonWithPreflight EvaluationDetail,
+    projectJobEvaluationDiff ::
+      f
+        :- Summary "Compare two evaluations"
+          :> Description "A list of attributes that have been added, removed or changed between two evaluations. Also lists changes to the IFD derivations."
+          :> "jobs"
+          :> Capture' '[Required, Strict] "jobId" (Id Job)
+          :> "evaluation"
+          :> "compare"
+          :> Capture' '[Required, Strict] "baseJobId" (Id Job)
+          :> auth
+          :> GetJsonWithPreflight EvaluationDiff,
     jobDerivationFailureGraph ::
       f
         :- Summary "Find all failures in an evaluation's derivations"
@@ -224,4 +245,5 @@
   deriving (Generic)
 
 newtype PagedJobs = PagedJobs (PagedResponse Job)
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Projects/CreateProject.hs b/src/Hercules/API/Projects/CreateProject.hs
--- a/src/Hercules/API/Projects/CreateProject.hs
+++ b/src/Hercules/API/Projects/CreateProject.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Projects.CreateProject where
 
@@ -9,4 +10,5 @@
   { primaryRepoId :: Id Repo,
     enabled :: Bool
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Projects/CreateUserEffectTokenResponse.hs b/src/Hercules/API/Projects/CreateUserEffectTokenResponse.hs
--- a/src/Hercules/API/Projects/CreateUserEffectTokenResponse.hs
+++ b/src/Hercules/API/Projects/CreateUserEffectTokenResponse.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Projects.CreateUserEffectTokenResponse where
 
@@ -7,4 +8,5 @@
 data CreateUserEffectTokenResponse = CreateUserEffectTokenResponse
   { token :: Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Projects/Job.hs b/src/Hercules/API/Projects/Job.hs
--- a/src/Hercules/API/Projects/Job.hs
+++ b/src/Hercules/API/Projects/Job.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Projects.Job
   ( module Hercules.API.Projects.Job,
@@ -11,6 +12,7 @@
 import Hercules.API.Evaluation.Evaluation
   ( Evaluation,
   )
+import Hercules.API.Forge.Forge (Forge)
 import Hercules.API.Inputs.ImmutableInput (ImmutableInput)
 import Hercules.API.Prelude
 import Hercules.API.Projects.Project (Project)
@@ -21,6 +23,9 @@
   { id :: Id Job,
     projectId :: Id Project,
     index :: Int64,
+    repoName :: Name Project,
+    ownerName :: Name Account,
+    forgeName :: Name Forge,
     repoId :: Id Repo,
     startTime :: UTCTime,
     endTime :: Maybe UTCTime,
@@ -43,14 +48,16 @@
     mayCancel :: Bool,
     mayRerun :: Bool
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data JobType
   = Config
   | Legacy
   | OnPush
   | OnSchedule
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data GitCommitSource = GitCommitSource
   { revision :: Text,
@@ -60,10 +67,12 @@
     committer :: Maybe Account,
     link :: Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data ProjectAndJobs = ProjectAndJobs
   { project :: Project,
     jobs :: [Job]
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Projects/JobHandlers.hs b/src/Hercules/API/Projects/JobHandlers.hs
--- a/src/Hercules/API/Projects/JobHandlers.hs
+++ b/src/Hercules/API/Projects/JobHandlers.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Projects.JobHandlers
   ( JobHandlers (..),
@@ -15,4 +16,5 @@
     onPush :: Map Text OnPushHandler,
     onSchedule :: Map Text OnScheduleHandler
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Projects/JobHandlers/OnPushHandler.hs b/src/Hercules/API/Projects/JobHandlers/OnPushHandler.hs
--- a/src/Hercules/API/Projects/JobHandlers/OnPushHandler.hs
+++ b/src/Hercules/API/Projects/JobHandlers/OnPushHandler.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Projects.JobHandlers.OnPushHandler
   ( OnPushHandler (..),
@@ -11,4 +12,5 @@
   { name :: Text,
     isFlake :: Bool
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Projects/JobHandlers/OnScheduleHandler.hs b/src/Hercules/API/Projects/JobHandlers/OnScheduleHandler.hs
--- a/src/Hercules/API/Projects/JobHandlers/OnScheduleHandler.hs
+++ b/src/Hercules/API/Projects/JobHandlers/OnScheduleHandler.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Projects.JobHandlers.OnScheduleHandler
   ( OnScheduleHandler (..),
@@ -15,7 +16,8 @@
     when :: TimeConstraints,
     mainExists :: Bool
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data TimeConstraints = TimeConstraints
   { minute :: Int,
@@ -23,4 +25,5 @@
     dayOfWeek :: Maybe [DayOfWeek],
     dayOfMonth :: Maybe [Int]
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Projects/LegacySimpleJob.hs b/src/Hercules/API/Projects/LegacySimpleJob.hs
--- a/src/Hercules/API/Projects/LegacySimpleJob.hs
+++ b/src/Hercules/API/Projects/LegacySimpleJob.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Projects.LegacySimpleJob where
 
@@ -15,4 +16,5 @@
     status :: JobStatus,
     phase :: JobPhase
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Projects/PatchProject.hs b/src/Hercules/API/Projects/PatchProject.hs
--- a/src/Hercules/API/Projects/PatchProject.hs
+++ b/src/Hercules/API/Projects/PatchProject.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Projects.PatchProject where
 
@@ -8,4 +9,5 @@
 data PatchProject = PatchProject
   { enabled :: Maybe Bool
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Projects/Project.hs b/src/Hercules/API/Projects/Project.hs
--- a/src/Hercules/API/Projects/Project.hs
+++ b/src/Hercules/API/Projects/Project.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Projects.Project where
 
@@ -25,4 +26,5 @@
     --   job statuses etc.
     isPublic :: Bool
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Projects/SimpleJob.hs b/src/Hercules/API/Projects/SimpleJob.hs
--- a/src/Hercules/API/Projects/SimpleJob.hs
+++ b/src/Hercules/API/Projects/SimpleJob.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Projects.SimpleJob where
 
@@ -13,7 +14,8 @@
     status :: JobStatus,
     phase :: JobPhase
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data JobPhase
   = Queued
@@ -21,13 +23,15 @@
   | Building
   | Effects
   | Done
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data JobStatus
   = Pending
   | Failure
   | Success
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 -- | Whichever is "worse": 'Failure' wins out, otherwise 'Pending' wins out, otherwise all are 'Success'.
 instance Semigroup JobStatus where
diff --git a/src/Hercules/API/Projects/SimpleProject.hs b/src/Hercules/API/Projects/SimpleProject.hs
--- a/src/Hercules/API/Projects/SimpleProject.hs
+++ b/src/Hercules/API/Projects/SimpleProject.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Projects.SimpleProject where
 
@@ -18,4 +19,5 @@
     --   job statuses etc.
     isPublic :: Bool
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Repos/Repo.hs b/src/Hercules/API/Repos/Repo.hs
--- a/src/Hercules/API/Repos/Repo.hs
+++ b/src/Hercules/API/Repos/Repo.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Repos.Repo where
 
@@ -26,4 +27,5 @@
     --   repository
     isInstallable :: Bool
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Repos/RepoKey.hs b/src/Hercules/API/Repos/RepoKey.hs
--- a/src/Hercules/API/Repos/RepoKey.hs
+++ b/src/Hercules/API/Repos/RepoKey.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Repos.RepoKey where
 
@@ -11,4 +12,5 @@
     repoName :: Text,
     projectId :: Maybe (Id Project)
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/Repos/SimpleRepo.hs b/src/Hercules/API/Repos/SimpleRepo.hs
--- a/src/Hercules/API/Repos/SimpleRepo.hs
+++ b/src/Hercules/API/Repos/SimpleRepo.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.Repos.SimpleRepo where
 
@@ -14,4 +15,5 @@
     imageURL :: Maybe Text,
     isPublic :: Bool
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/SimpleAttribute.hs b/src/Hercules/API/SimpleAttribute.hs
new file mode 100644
--- /dev/null
+++ b/src/Hercules/API/SimpleAttribute.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE StandaloneDeriving #-}
+
+module Hercules.API.SimpleAttribute
+  ( SimpleAttribute (..),
+  )
+where
+
+import Hercules.API.Prelude
+import Prelude ()
+
+-- | NOTE: Generic types must always be wrapped in a newtype, so as to avoid ambiguities in the generated schema.
+data SimpleAttribute a = SimpleAttribute
+  { path :: [Text],
+    value :: a
+  }
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, FromJSON, ToJSON)
+
+deriving instance ToSchema a => ToSchema (SimpleAttribute a)
+
+deriving instance Functor SimpleAttribute
+
+deriving instance Foldable SimpleAttribute
+
+deriving instance Traversable SimpleAttribute
diff --git a/src/Hercules/API/State/ProjectState.hs b/src/Hercules/API/State/ProjectState.hs
--- a/src/Hercules/API/State/ProjectState.hs
+++ b/src/Hercules/API/State/ProjectState.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.State.ProjectState where
 
@@ -10,4 +11,5 @@
   { projectId :: Id Project,
     stateFiles :: Map Text StateFile
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/State/StateFile.hs b/src/Hercules/API/State/StateFile.hs
--- a/src/Hercules/API/State/StateFile.hs
+++ b/src/Hercules/API/State/StateFile.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.State.StateFile where
 
@@ -11,4 +12,5 @@
     name :: Text,
     versions :: [StateVersion]
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/State/StateLockAcquireRequest.hs b/src/Hercules/API/State/StateLockAcquireRequest.hs
--- a/src/Hercules/API/State/StateLockAcquireRequest.hs
+++ b/src/Hercules/API/State/StateLockAcquireRequest.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.State.StateLockAcquireRequest where
 
@@ -17,4 +18,5 @@
     -- | Generate a random key to make sure that a retry can be successful.
     idempotencyKey :: Maybe (Id "IdempotencyKey")
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/State/StateLockAcquireResponse.hs b/src/Hercules/API/State/StateLockAcquireResponse.hs
--- a/src/Hercules/API/State/StateLockAcquireResponse.hs
+++ b/src/Hercules/API/State/StateLockAcquireResponse.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.State.StateLockAcquireResponse where
 
@@ -9,15 +10,18 @@
 data StateLockAcquireResponse
   = Acquired StateLockAcquiredResponse
   | Blocked StateLockBlockedResponse
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data StateLockAcquiredResponse = StateLockAcquiredResponse
   { leaseId :: Id "StateLockLease",
     expirationTime :: UTCTime
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
 
 data StateLockBlockedResponse = LockBlockedResponse
   { blockedByLeases :: [StateLockLease]
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/State/StateLockLease.hs b/src/Hercules/API/State/StateLockLease.hs
--- a/src/Hercules/API/State/StateLockLease.hs
+++ b/src/Hercules/API/State/StateLockLease.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.State.StateLockLease where
 
@@ -25,4 +26,5 @@
     -- | Whether the lock is exclusive.
     exclusive :: Bool
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/State/StateLockUpdateRequest.hs b/src/Hercules/API/State/StateLockUpdateRequest.hs
--- a/src/Hercules/API/State/StateLockUpdateRequest.hs
+++ b/src/Hercules/API/State/StateLockUpdateRequest.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.State.StateLockUpdateRequest where
 
@@ -8,4 +9,5 @@
 data StateLockUpdateRequest = StateLockUpdateRequest
   { description :: Maybe Text
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/API/State/StateVersion.hs b/src/Hercules/API/State/StateVersion.hs
--- a/src/Hercules/API/State/StateVersion.hs
+++ b/src/Hercules/API/State/StateVersion.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DerivingStrategies #-}
 
 module Hercules.API.State.StateVersion where
 
@@ -20,4 +21,5 @@
     sha256 :: Maybe Text,
     size :: Maybe Int
   }
-  deriving (Generic, Show, Eq, NFData, ToJSON, FromJSON, ToSchema)
+  deriving (Generic, Show, Eq)
+  deriving anyclass (NFData, ToJSON, FromJSON, ToSchema)
diff --git a/src/Hercules/Frontend.hs b/src/Hercules/Frontend.hs
--- a/src/Hercules/Frontend.hs
+++ b/src/Hercules/Frontend.hs
@@ -2,7 +2,7 @@
 
 module Hercules.Frontend where
 
-import qualified Data.Text as T
+import Data.Text qualified as T
 import Hercules.API.Accounts.Account (Account)
 import Hercules.API.Auth (AuthRoutes)
 import Hercules.API.Forge.Forge
diff --git a/test/Hercules/API/AttributeSpec.hs b/test/Hercules/API/AttributeSpec.hs
--- a/test/Hercules/API/AttributeSpec.hs
+++ b/test/Hercules/API/AttributeSpec.hs
@@ -2,7 +2,7 @@
 
 module Hercules.API.AttributeSpec (spec) where
 
-import qualified Data.Text as T
+import Data.Text qualified as T
 import Hercules.API.Attribute
 import Protolude
 import Test.Hspec
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -1,7 +1,7 @@
 module Main where
 
 import Protolude
-import qualified Spec
+import Spec qualified
 import Test.Hspec.Runner
 
 main :: IO ()
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -2,7 +2,7 @@
 
 module Spec where
 
-import qualified Hercules.API.AttributeSpec
+import Hercules.API.AttributeSpec qualified
 import Test.Hspec
 
 spec :: Spec
