diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,30 @@
 # Revision history for ogma-core
 
+## [1.16.0] - 2026-09-21
+
+* Version bump (1.16.0) (#606).
+* Replace adhoc function `mergeMaybe` with function from `base` (#516).
+* Fix malformed comments (#518).
+* Remove unnecessary line breaks (#520).
+* Add missing periods to Haddock comments (#524).
+* Fix spelling of F Prime (#526).
+* Replace explicit recursion with calls to `base:Control.Monad.foldM` (#532).
+* Add valid Haddock documentation to all top-level functions (#542).
+* Bump upper version constraints on `QuickCheck`, `megaparsec` (#545).
+* Make `Dockerfile` base image in default ROS 2 template customizable (#548).
+* Allow for inputs to be deeply nested fields in ROS 2 template (#547).
+* Adjust ROS 2 `Dockerfile` to run `rosdep init` only when needed (#561).
+* Fix incorrect creation of two instances of ROS 2 monitoring node (#564).
+* Increase detail in template expansion error messages (#390).
+* Use path prefixes to specify path resolution context in project files (#567).
+* Update `Dockerfile` in ROS 2 template for Space ROS `jazzy-2026.07.0` (#569).
+* Introduce default input format configuration files (#575).
+* Address HLint suggestions (#586).
+* Remove unused imports (#590).
+* Remove unused pragma (#600).
+* De-duplicate cFS application data (#602).
+* Fix `Command.Common.mergeSpecs` to avoid dropping externs (#551).
+
 ## [1.15.0] - 2026-07-21
 
 * Version bump (1.15.0) (#508).
diff --git a/data/formats/default_literal b/data/formats/default_literal
new file mode 100644
--- /dev/null
+++ b/data/formats/default_literal
@@ -0,0 +1,15 @@
+JSONFormat
+   { specInternalVars          = Just "..auxiliary_defs[*]"
+   , specInternalVarId         = ".name"
+   , specInternalVarExpr       = ".meaning"
+   , specInternalVarType       = Just ".type"
+   , specExternalVars          = Just "..inputs[*]"
+   , specExternalVarId         = ".name"
+   , specExternalVarType       = Just ".type"
+   , specRequirements          = "..properties[*]"
+   , specRequirementId         = ".id"
+   , specRequirementDesc       = Just ".description"
+   , specRequirementExpr       = ".formula"
+   , specRequirementResultType = Nothing
+   , specRequirementResultExpr = Nothing
+   }
diff --git a/data/formats/default_lustre b/data/formats/default_lustre
new file mode 100644
--- /dev/null
+++ b/data/formats/default_lustre
@@ -0,0 +1,15 @@
+JSONFormat
+   { specInternalVars          = Just "..auxiliary_defs[*]"
+   , specInternalVarId         = ".name"
+   , specInternalVarExpr       = ".meaning"
+   , specInternalVarType       = Just ".type"
+   , specExternalVars          = Just "..inputs[*]"
+   , specExternalVarId         = ".name"
+   , specExternalVarType       = Just ".type"
+   , specRequirements          = "..properties[*]"
+   , specRequirementId         = ".id"
+   , specRequirementDesc       = Just ".description"
+   , specRequirementExpr       = ".formula"
+   , specRequirementResultType = Nothing
+   , specRequirementResultExpr = Nothing
+   }
diff --git a/data/formats/default_smv b/data/formats/default_smv
new file mode 100644
--- /dev/null
+++ b/data/formats/default_smv
@@ -0,0 +1,15 @@
+JSONFormat
+   { specInternalVars          = Just "..auxiliary_defs[*]"
+   , specInternalVarId         = ".name"
+   , specInternalVarExpr       = ".meaning"
+   , specInternalVarType       = Just ".type"
+   , specExternalVars          = Just "..inputs[*]"
+   , specExternalVarId         = ".name"
+   , specExternalVarType       = Just ".type"
+   , specRequirements          = "..properties[*]"
+   , specRequirementId         = ".id"
+   , specRequirementDesc       = Just ".description"
+   , specRequirementExpr       = ".formula"
+   , specRequirementResultType = Nothing
+   , specRequirementResultExpr = Nothing
+   }
diff --git a/ogma-core.cabal b/ogma-core.cabal
--- a/ogma-core.cabal
+++ b/ogma-core.cabal
@@ -19,7 +19,7 @@
 build-type:          Simple
 
 name:                ogma-core
-version:             1.15.0
+version:             1.16.0
 homepage:            https://github.com/nasa/ogma
 bug-reports:         https://github.com/nasa/ogma/issues
 license:             Apache-2.0
@@ -76,6 +76,9 @@
                      templates/fprime/instance-copilot
                      templates/report/Report.md
                      templates/standalone/Copilot.hs
+                     data/formats/default_smv
+                     data/formats/default_literal
+                     data/formats/default_lustre
                      data/formats/fcs_smv
                      data/formats/fcs_lustre
                      data/formats/fdb_smv
@@ -160,23 +163,23 @@
     , filepath                >= 1.4.2    && < 1.6
     , graphviz                >= 2999.20  && < 2999.21
     , hint                    >= 0.9.0    && < 1.10
-    , megaparsec              >= 8.0.0    && < 9.8
+    , megaparsec              >= 8.0.0    && < 9.9
     , mtl                     >= 2.2.2    && < 2.4
     , process                 >= 1.6      && < 1.7
     , text                    >= 1.2.3.1  && < 2.2
     , vector                  >= 0.12.0.1 && < 0.14
     , yaml                    >= 0.11.7.0 && < 0.12
 
-    , ogma-extra              >= 1.15.0 && < 1.16
-    , ogma-language-c         >= 1.15.0 && < 1.16
-    , ogma-language-copilot   >= 1.15.0 && < 1.16
-    , ogma-language-csv       >= 1.15.0 && < 1.16
-    , ogma-language-jsonspec  >= 1.15.0 && < 1.16
-    , ogma-language-lustre    >= 1.15.0 && < 1.16
-    , ogma-language-smv       >= 1.15.0 && < 1.16
-    , ogma-language-xlsx      >= 1.15.0 && < 1.16
-    , ogma-language-xmlspec   >= 1.15.0 && < 1.16
-    , ogma-spec               >= 1.15.0 && < 1.16
+    , ogma-extra              >= 1.16.0 && < 1.17
+    , ogma-language-c         >= 1.16.0 && < 1.17
+    , ogma-language-copilot   >= 1.16.0 && < 1.17
+    , ogma-language-csv       >= 1.16.0 && < 1.17
+    , ogma-language-jsonspec  >= 1.16.0 && < 1.17
+    , ogma-language-lustre    >= 1.16.0 && < 1.17
+    , ogma-language-smv       >= 1.16.0 && < 1.17
+    , ogma-language-xlsx      >= 1.16.0 && < 1.17
+    , ogma-language-xmlspec   >= 1.16.0 && < 1.17
+    , ogma-spec               >= 1.16.0 && < 1.17
 
   hs-source-dirs:
     src
@@ -198,7 +201,7 @@
       base                       >= 4.11.0.0 && < 5
     , directory                  >= 1.3.1.5  && < 1.4
     , HUnit                      >= 1.2.0.0  && < 1.7
-    , QuickCheck                 >= 2.8.2    && < 2.17
+    , QuickCheck                 >= 2.8.2    && < 2.19
     , test-framework             >= 0.8.2    && < 0.9
     , test-framework-hunit       >= 0.2.0    && < 0.4
     , test-framework-quickcheck2 >= 0.3.0.4  && < 0.4
diff --git a/src/Command/CFSApp.hs b/src/Command/CFSApp.hs
--- a/src/Command/CFSApp.hs
+++ b/src/Command/CFSApp.hs
@@ -36,11 +36,12 @@
   where
 
 -- External imports
-import           Control.Applicative    ( liftA2, (<|>) )
+import           Control.Applicative    ( (<|>) )
 import qualified Control.Exception      as E
 import           Control.Monad.Except   ( ExceptT (..), liftEither,
                                           throwError )
 import           Data.Aeson             ( ToJSON (..), Value )
+import           Data.List              ( nub )
 import           Data.Maybe             ( fromMaybe, mapMaybe, maybeToList )
 import           GHC.Generics           ( Generic )
 
@@ -49,14 +50,15 @@
 
 -- Internal imports: auxiliary
 import Command.Result         ( Result (..) )
+import Data.Either.Extra      ( mapLeft )
 import Data.List.Extra        ( stripSuffix )
 import Data.String.Extra      ( pascalCase )
 import System.Directory.Extra ( copyTemplate )
 
 -- Internal imports
-import Command.Common                 (InputFile (..), cannotCopyTemplate,
+import Command.Common                 (InputFile (..), cannotCopyTemplateF,
                                        checkArguments, combineInputFiles,
-                                       locateTemplateDir, makeLeftE,
+                                       locateTemplateDir,
                                        openVarDBFilesWithDefault,
                                        parseInputFile,
                                        parseRequirementsListFile,
@@ -89,7 +91,7 @@
     let subst = mergeObjects (toJSON appData) templateVars
 
     -- Expand template
-    ExceptT $ fmap (makeLeftE cannotCopyTemplate) $ E.try $
+    ExceptT $ fmap (mapLeft cannotCopyTemplateF) $ E.try $
       copyTemplate templateDir subst targetDir
 
   where
@@ -99,6 +101,8 @@
     functions     = exprPair (commandPropFormat options)
     templateVarsF = commandExtraVars options
 
+-- | Generate application data describing core elements of a new cFS
+-- application that implements the input requirements or diagrams.
 command' :: CommandOptions
          -> ExprPair
          -> ExceptT ErrorTriplet IO AppData
@@ -110,7 +114,7 @@
 
     specT <- maybe
                (return Nothing)
-               (\e -> Just . InputFileSpec <$> readInputExpr' e)
+               (fmap (Just . InputFileSpec) . readInputExpr')
                cExpr
 
     specF <- if null fpA
@@ -128,8 +132,7 @@
 
     mode <- parseDiagramMode (commandDiagramMode options)
 
-    copilotM <- sequenceA $
-                  (\spec' -> processSpec spec' cExpr fpA mode) <$> spec
+    copilotM <- traverse (\spec' -> processSpec spec' cExpr fpA mode) spec
 
     let varNames = fromMaybe (defaultVarNames spec) vs
         monitors = maybe (defaultMonitors spec) (map (\x -> (x, Nothing))) rs
@@ -156,8 +159,8 @@
     readInputFile' f =
       parseInputFile f formatName propFormatName propVia exprT
 
-    processSpec spec' expr' fp' mode =
-      Command.Standalone.commandLogic expr' fp' "copilot" [] exprT spec' mode
+    processSpec spec' expr' fp' =
+      Command.Standalone.commandLogic expr' fp' "copilot" [] exprT spec'
 
     defaultVarNames spec = case spec of
       Just (InputFileSpec spec') -> specExtractExternalVariables (Just spec')
@@ -180,10 +183,17 @@
              -> [Trigger]
              -> Maybe Command.Standalone.AppData
              -> AppData
-commandLogic varDB varNames handlers copilotM =
-    AppData vars ids infos datas handlers copilotM
+commandLogic varDB varNames triggers =
+    AppData vars' ids' infos' datas' triggers'
+
   where
 
+    vars'     = nub vars
+    ids'      = nub ids
+    infos'    = nub infos
+    datas'    = nub datas
+    triggers' = nub triggers
+
     -- This is a Data.List.unzip4
     (vars, ids, infos, datas) = foldr f ([], [], [], []) varNames
 
@@ -245,7 +255,7 @@
 
       active = inputActive inputDef
 
-  let typeVar' = fromMaybe (topicType topicDef) (typeToType <$> typeDef)
+  let typeVar' = maybe (topicType topicDef) typeToType typeDef
 
   -- Pick name for the function to process a message ID.
   let mn = pascalCase $ stripSuffix "_MID" mid
@@ -272,7 +282,7 @@
     { varDeclName :: String
     , varDeclType :: String
     }
-  deriving (Generic)
+  deriving (Eq, Generic)
 
 instance ToJSON VarDecl
 
@@ -286,7 +296,7 @@
     , msgInfoDesc  :: String
     , msgInfoExtra :: Value
     }
-  deriving (Generic)
+  deriving (Eq, Generic)
 
 instance ToJSON MsgInfo
 
@@ -300,7 +310,7 @@
     , msgDataVarType   :: String
     , msgDataActive    :: Bool
     }
-  deriving (Generic)
+  deriving (Eq, Generic)
 
 instance ToJSON MsgData
 
@@ -310,7 +320,7 @@
     , triggerType    :: Maybe String
     , triggerMsgType :: Maybe String
     }
-  deriving (Generic)
+  deriving (Eq, Generic)
 
 instance ToJSON Trigger
 
diff --git a/src/Command/Common.hs b/src/Command/Common.hs
--- a/src/Command/Common.hs
+++ b/src/Command/Common.hs
@@ -1,5 +1,4 @@
 {-# LANGUAGE ExistentialQuantification #-}
-{-# LANGUAGE OverloadedStrings         #-}
 {-# LANGUAGE ScopedTypeVariables       #-}
 -- Copyright 2022 United States Government as represented by the Administrator
 -- of the National Aeronautics and Space Administration. All Rights Reserved.
@@ -32,8 +31,7 @@
     , specExtractExternalVariables
     , specExtractHandlers
     , processResult
-    , cannotCopyTemplate
-    , makeLeftE
+    , cannotCopyTemplateF
     , locateTemplateDir
     )
   where
@@ -47,8 +45,9 @@
 import           System.FilePath        ((</>))
 
 -- External imports: auxiliary
-import Data.ByteString.Extra as B (safeReadFile)
-import Data.String.Extra     (sanitizeLCIdentifier, sanitizeUCIdentifier)
+import Data.ByteString.Extra  as B (safeReadFile)
+import Data.String.Extra      (sanitizeLCIdentifier, sanitizeUCIdentifier)
+import System.Directory.Extra (CopyTemplateException(..))
 
 -- External imports: ogma
 import Data.OgmaSpec (Requirement (..), Spec (..), externalVariableName,
@@ -98,7 +97,7 @@
     mergeSpecs :: Spec a -> Spec a -> Spec a
     mergeSpecs s1 s2 = Spec
       { internalVariables = internalVariables s1 ++ internalVariables s2
-      , externalVariables = externalVariables s2 ++ externalVariables s2
+      , externalVariables = externalVariables s1 ++ externalVariables s2
       , requirements      = requirements s1 ++ requirements s2
       }
 
@@ -157,7 +156,7 @@
 parseRequirementsListFile Nothing   = return Nothing
 parseRequirementsListFile (Just fp) =
   ExceptT $ makeLeftE (cannotOpenHandlersFile fp) <$>
-    (E.try $ Just . lines <$> readFile fp)
+    E.try (Just . lines <$> readFile fp)
 
 -- | Read a list of variable DBs.
 openVarDBFiles :: VariableDB
@@ -226,9 +225,8 @@
 -- | Extract the variables from a specification, and sanitize them.
 specExtractExternalVariables :: Maybe (Spec a) -> [String]
 specExtractExternalVariables Nothing   = []
-specExtractExternalVariables (Just cs) = map sanitizeLCIdentifier
-                                       $ map externalVariableName
-                                       $ externalVariables cs
+specExtractExternalVariables (Just cs) =
+ map (sanitizeLCIdentifier . externalVariableName) $ externalVariables cs
 
 -- | Extract the requirements from a specification, and sanitize them to match
 -- the names of the handlers used by Copilot.
@@ -313,14 +311,12 @@
 
 -- | Exception handler to deal with the case of files that cannot be
 -- copied/generated due lack of space or permissions or some I/O error.
-cannotCopyTemplate :: ErrorTriplet
-cannotCopyTemplate =
-    ErrorTriplet ecCannotCopyTemplate msg LocationNothing
+cannotCopyTemplateF :: CopyTemplateException -> ErrorTriplet
+cannotCopyTemplateF e =
+    ErrorTriplet ecCannotCopyTemplate (msg ++ show e) LocationNothing
   where
     msg =
-      "Generation failed during copy/write operation. Check that"
-      ++ " there's free space in the disk and that you have the necessary"
-      ++ " permissions to write in the destination directory."
+      "Generation failed during copy/write operation: "
 
 -- ** Error codes
 
diff --git a/src/Command/Diagram.hs b/src/Command/Diagram.hs
--- a/src/Command/Diagram.hs
+++ b/src/Command/Diagram.hs
@@ -187,7 +187,7 @@
 exprPair Inputs = ExprPair $
   ExprPairT
     ((Right . read) :: String -> Either String Int)
-    (\_ -> id)
+    (const id)
     (\x -> "input == " ++ show x)
     (const [])
     (-1)
diff --git a/src/Command/FPrimeApp.hs b/src/Command/FPrimeApp.hs
--- a/src/Command/FPrimeApp.hs
+++ b/src/Command/FPrimeApp.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE DeriveGeneric             #-}
 {-# LANGUAGE ExistentialQuantification #-}
-{-# LANGUAGE OverloadedStrings         #-}
 {-# LANGUAGE ScopedTypeVariables       #-}
 -- Copyright 2022 United States Government as represented by the Administrator
 -- of the National Aeronautics and Space Administration. All Rights Reserved.
@@ -19,7 +18,7 @@
 -- License for the specific language governing permissions and limitations
 -- under the License.
 --
--- | Create <https://github.com/nasa/fprime FPrime> components that subscribe
+-- | Create <https://github.com/nasa/fprime F Prime> components that subscribe
 -- to obtain data and call Copilot when new values arrive.
 
 {- HLINT ignore "Functor law" -}
@@ -31,7 +30,7 @@
   where
 
 -- External imports
-import           Control.Applicative    ( liftA2, (<|>) )
+import           Control.Applicative    ( (<|>) )
 import qualified Control.Exception      as E
 import           Control.Monad.Except   ( ExceptT(..), liftEither )
 import           Data.Aeson             ( ToJSON, toJSON )
@@ -40,6 +39,7 @@
 import           GHC.Generics           ( Generic )
 
 -- External imports: auxiliary
+import Data.Either.Extra      ( mapLeft )
 import System.Directory.Extra ( copyTemplate )
 
 import qualified Command.Standalone
@@ -48,9 +48,9 @@
 import Command.Result (Result (..))
 
 -- Internal imports
-import Command.Common                 (InputFile (..), cannotCopyTemplate,
+import Command.Common                 (InputFile (..), cannotCopyTemplateF,
                                        checkArguments, combineInputFiles,
-                                       locateTemplateDir, makeLeftE,
+                                       locateTemplateDir,
                                        openVarDBFilesWithDefault,
                                        parseInputFile,
                                        parseRequirementsListFile,
@@ -67,7 +67,7 @@
 import Data.Spec.Parser               (readInputExpr)
 import Language.Trans.Diagram2Copilot (DiagramMode (..))
 
--- | Generate a new FPrime component connected to Copilot.
+-- | Generate a new F Prime component connected to Copilot.
 command :: CommandOptions -- ^ Options to the ROS backend.
         -> IO (Result ErrorCode)
 command options = processResult $ do
@@ -81,7 +81,7 @@
     let subst = mergeObjects (toJSON appData) templateVars
 
     -- Expand template
-    ExceptT $ fmap (makeLeftE cannotCopyTemplate) $ E.try $
+    ExceptT $ fmap (mapLeft cannotCopyTemplateF) $ E.try $
       copyTemplate templateDir subst targetDir
 
   where
@@ -91,6 +91,8 @@
     functions     = exprPair (commandPropFormat options)
     templateVarsF = commandExtraVars options
 
+-- | Generate application data describing core elements of a new F Prime
+-- component that implements the input requirements or diagrams.
 command' :: CommandOptions
          -> ExprPair
          -> ExceptT ErrorTriplet IO AppData
@@ -102,7 +104,7 @@
 
     specT <- maybe
                (return Nothing)
-               (\e -> Just . InputFileSpec <$> readInputExpr' e)
+               (fmap (Just . InputFileSpec). readInputExpr')
                cExpr
 
     specF <- if null fpA
@@ -118,7 +120,7 @@
 
     liftEither $ checkArguments spec vs rs
 
-    copilotM <- sequenceA $ (\spec' -> processSpec spec' cExpr fpA) <$> spec
+    copilotM <- traverse (\spec' -> processSpec spec' cExpr fpA) spec
 
     let varNames = fromMaybe (defaultVarNames spec) vs
         monitors = maybe (defaultMonitors spec) (map (\x -> (x, Nothing))) rs
diff --git a/src/Command/Overview.hs b/src/Command/Overview.hs
--- a/src/Command/Overview.hs
+++ b/src/Command/Overview.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE DeriveGeneric             #-}
 {-# LANGUAGE ExistentialQuantification #-}
-{-# LANGUAGE OverloadedStrings         #-}
 {-# LANGUAGE ScopedTypeVariables       #-}
 -- Copyright 2024 United States Government as represented by the Administrator
 -- of the National Aeronautics and Space Administration. All Rights Reserved.
@@ -202,7 +201,7 @@
 instance ToJSON CommandSummaryDiagram
 
 -- | Options used to customize the interpretation of input specifications.
-data CommandOptions = CommandOptions
+newtype CommandOptions = CommandOptions
   { commandInputFiles :: [ OverviewFile ]
   }
 
diff --git a/src/Command/ROSApp.hs b/src/Command/ROSApp.hs
--- a/src/Command/ROSApp.hs
+++ b/src/Command/ROSApp.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE DeriveGeneric             #-}
 {-# LANGUAGE ExistentialQuantification #-}
-{-# LANGUAGE OverloadedStrings         #-}
 {-# LANGUAGE ScopedTypeVariables       #-}
 -- Copyright 2022 United States Government as represented by the Administrator
 -- of the National Aeronautics and Space Administration. All Rights Reserved.
@@ -36,7 +35,7 @@
   where
 
 -- External imports
-import           Control.Applicative  (liftA2, (<|>))
+import           Control.Applicative  ((<|>))
 import qualified Control.Exception    as E
 import           Control.Monad.Except (ExceptT (..), liftEither)
 import           Data.Aeson           (ToJSON (..))
@@ -49,12 +48,13 @@
 import qualified Command.Standalone
 
 -- Internal imports: auxiliary
-import Command.Result (Result (..))
+import Command.Result    ( Result (..) )
+import Data.Either.Extra ( mapLeft )
 
 -- Internal imports
-import Command.Common                 (InputFile (..), cannotCopyTemplate,
+import Command.Common                 (InputFile (..), cannotCopyTemplateF,
                                        checkArguments, combineInputFiles,
-                                       locateTemplateDir, makeLeftE,
+                                       locateTemplateDir,
                                        openVarDBFilesWithDefault,
                                        parseInputFile,
                                        parseRequirementsListFile,
@@ -87,7 +87,7 @@
     let subst = mergeObjects (toJSON appData) templateVars
 
     -- Expand template
-    ExceptT $ fmap (makeLeftE cannotCopyTemplate) $ E.try $
+    ExceptT $ fmap (mapLeft cannotCopyTemplateF) $ E.try $
       copyTemplate templateDir subst targetDir
 
   where
@@ -97,6 +97,8 @@
     functions     = exprPair (commandPropFormat options)
     templateVarsF = commandExtraVars options
 
+-- | Generate application data describing core elements of a new ROS package
+-- that implements the input requirements or diagrams.
 command' :: CommandOptions
          -> ExprPair
          -> ExceptT ErrorTriplet IO AppData
@@ -108,7 +110,7 @@
 
     specT <- maybe
                (return Nothing)
-               (\e -> Just . InputFileSpec <$> readInputExpr' e)
+               (fmap (Just . InputFileSpec) . readInputExpr')
                cExpr
 
     specF <- if null fpA
@@ -124,7 +126,7 @@
 
     liftEither $ checkArguments spec vs rs
 
-    copilotM <- sequenceA $ (\spec' -> processSpec spec' cExpr fpA) <$> spec
+    copilotM <- traverse (\spec' -> processSpec spec' cExpr fpA) spec
 
     let varNames = fromMaybe (defaultVarNames spec) vs
         monitors = maybe (defaultMonitors spec) (map (\x -> (x, Nothing))) rs
@@ -232,9 +234,10 @@
                 (inputType inputDef)
                 (Just . typeToType)
                 (findType varDB varName "ros/variable" "C")
-  let typeMsg' = fromMaybe
+  let typeMsg' = maybe
                    (topicType topicDef)
-                   (typeFromType <$> findType varDB varName "ros/message" "C")
+                   typeFromType
+                   (findType varDB varName "ros/message" "C")
 
       fieldMsg = typeFromField =<< findType varDB varName "ros/message" "C"
 
diff --git a/src/Command/Report.hs b/src/Command/Report.hs
--- a/src/Command/Report.hs
+++ b/src/Command/Report.hs
@@ -29,21 +29,21 @@
 -- External imports
 import qualified Control.Exception      as E
 import           Control.Monad          (foldM)
-import           Control.Monad.Except   (ExceptT (..), liftEither, runExceptT,
-                                         withExceptT)
+import           Control.Monad.Except   (ExceptT (..), liftEither, withExceptT)
 import           Control.Monad.IO.Class (liftIO)
 import           Data.Aeson             (ToJSON (..))
 import           GHC.Generics           (Generic)
 
 -- External imports: Ogma
+import Data.Either.Extra      (mapLeft)
 import Data.OgmaSpec          (Requirement (..), Spec (..))
 import Data.String.Extra      (sanitizeUCIdentifier)
 import System.Directory.Extra (copyTemplate)
 
 -- Internal imports
 import           Command.Common              (InputFile (..),
-                                              cannotCopyTemplate,
-                                              locateTemplateDir, makeLeftE,
+                                              cannotCopyTemplateF,
+                                              locateTemplateDir,
                                               parseInputFile, processResult)
 import           Command.Errors              (ErrorCode, ErrorTriplet (..))
 import           Command.Result              (Result (..))
@@ -76,7 +76,7 @@
                     (commandInputFiles options)
 
     -- Expand template
-    ExceptT $ fmap (makeLeftE cannotCopyTemplate) $ E.try $
+    ExceptT $ fmap (mapLeft cannotCopyTemplateF) $ E.try $
       copyTemplate templateDir (toJSON reportData) targetDir
 
   where
@@ -159,7 +159,7 @@
                            }
 
         pure $ CommandSummary
-                 { commandRequirementsAny = length reqListDetails > 0
+                 { commandRequirementsAny = not (null reqListDetails)
                  , commandRequirementList = [fileReqs]
                  , commandDiagramsAny     = False
                  , commandDiagramsList    = []
diff --git a/src/Command/Result.hs b/src/Command/Result.hs
--- a/src/Command/Result.hs
+++ b/src/Command/Result.hs
@@ -26,7 +26,7 @@
 -- Internal imports
 import Data.Location ( Location )
 
--- | Result of the global process
+-- | Result of the global process.
 data Result a = Success
               | Error a String Location
 
diff --git a/src/Command/Search.hs b/src/Command/Search.hs
--- a/src/Command/Search.hs
+++ b/src/Command/Search.hs
@@ -174,7 +174,7 @@
 instance ToJSON RequirementInfo
 
 -- | Information about a diagram that matches the search query.
-data DiagramInfo = DiagramInfo
+newtype DiagramInfo = DiagramInfo
     { diagramInfoLocation :: FilePath
     }
   deriving (Generic, Show)
diff --git a/src/Command/Standalone.hs b/src/Command/Standalone.hs
--- a/src/Command/Standalone.hs
+++ b/src/Command/Standalone.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE DeriveGeneric             #-}
 {-# LANGUAGE ExistentialQuantification #-}
-{-# LANGUAGE OverloadedStrings         #-}
 {-# LANGUAGE ScopedTypeVariables       #-}
 -- Copyright 2020 United States Government as represented by the Administrator
 -- of the National Aeronautics and Space Administration. All Rights Reserved.
@@ -41,10 +40,10 @@
 import System.Directory.Extra (copyTemplate)
 
 -- Internal imports
-import Command.Common                 (InputFile (..), cannotCopyTemplate,
+import Command.Common                 (InputFile (..), cannotCopyTemplateF,
                                        combineInputFiles, locateTemplateDir,
-                                       makeLeftE, parseInputFile,
-                                       parseTemplateVarsFile, processResult)
+                                       parseInputFile, parseTemplateVarsFile,
+                                       processResult)
 import Command.Errors                 (ErrorCode, ErrorTriplet (..))
 import Command.Result                 (Result (..))
 import Data.Aeson.Extra               (mergeObjects)
@@ -78,7 +77,7 @@
     let subst = mergeObjects (toJSON appData) templateVars
 
     -- Expand template
-    ExceptT $ fmap (makeLeftE cannotCopyTemplate) $ E.try $
+    ExceptT $ fmap (mapLeft cannotCopyTemplateF) $ E.try $
       copyTemplate templateDir subst targetDir
 
   where
@@ -106,7 +105,7 @@
     -- definitions.
     specT <- maybe
                (return Nothing)
-               (\e -> Just . InputFileSpec <$> readInputExpr' e)
+               (fmap (Just . InputFileSpec) . readInputExpr')
                triggerExprM
 
     specF <- if null fpA
@@ -121,7 +120,7 @@
     let spec = specT <|> specF
 
     case spec of
-      Nothing    -> liftEither $ Left $ commandMissingSpec
+      Nothing    -> liftEither $ Left commandMissingSpec
       Just spec' ->
         commandLogic triggerExprM fpA name typeMaps exprT spec' ComputeState
 
diff --git a/src/Command/VariableDB.hs b/src/Command/VariableDB.hs
--- a/src/Command/VariableDB.hs
+++ b/src/Command/VariableDB.hs
@@ -36,6 +36,8 @@
   where
 
 -- External imports
+import Control.Applicative  ((<|>))
+import Control.Monad        (foldM)
 import Control.Monad.Except (ExceptT, throwError)
 import Data.Aeson           (FromJSON (..), Value (Object), (.:), withObject)
 import Data.Aeson.KeyMap    (filterWithKey)
@@ -104,8 +106,7 @@
 
 -- | Find an input with a given name.
 findInput :: VariableDB -> String -> Maybe InputDef
-findInput varDB name =
-  find (\x -> inputName x == name) (inputs varDB)
+findInput varDB name = find (\x -> inputName x == name) (inputs varDB)
 
 -- | Find a connection a given scope.
 findConnection :: InputDef -> String -> Maybe Connection
@@ -188,10 +189,7 @@
 -- contradictory information.
 mergeInputs :: Monad m
             => [InputDef] -> [InputDef] -> ExceptT ErrorTriplet m [InputDef]
-mergeInputs is1 []     = return is1
-mergeInputs is1 (i2:is2) = do
-  is1' <- mergeInput is1 i2
-  mergeInputs is1' is2
+mergeInputs = foldM mergeInput
 
 -- | Merge an input definition into a list of input definitions, so long as it
 -- does not contain contradictory information.
@@ -205,10 +203,8 @@
        || inputType i1 == inputType i2
        )
   = do cs <- mergeConnections (inputConnections i1) (inputConnections i2)
-       let i1' = i1 { inputType =
-                        mergeMaybe (inputType i1) (inputType i2)
-                    , inputConnections =
-                        cs
+       let i1' = i1 { inputType        = inputType i1 <|> inputType i2
+                    , inputConnections = cs
                     }
        return (i1' : is1)
 
@@ -220,10 +216,7 @@
 -- contradictory information.
 mergeConnections :: Monad m
                  => [Connection] -> [Connection] -> ExceptT ErrorTriplet m [Connection]
-mergeConnections cs1 []       = return cs1
-mergeConnections cs1 (c2:cs2) = do
-  cs1' <- mergeConnection cs1 c2
-  mergeConnections cs1' cs2
+mergeConnections = foldM mergeConnection
 
 -- | Merge a connection into a list of connections, so long as it does not
 -- contain contradictory information.
@@ -246,10 +239,7 @@
 -- information.
 mergeTopics :: Monad m
             => [TopicDef] -> [TopicDef] -> ExceptT ErrorTriplet m [TopicDef]
-mergeTopics ts1 [] = return ts1
-mergeTopics ts1 (t2:ts2) = do
-  ts1' <- mergeTopic ts1 t2
-  mergeTopics ts1' ts2
+mergeTopics = foldM mergeTopic
 
 -- | Merge a topic into a list of topics, so long as it does not contain
 -- contradictory information.
@@ -272,10 +262,7 @@
 -- contradictory information.
 mergeTypes :: Monad m
            => [TypeDef] -> [TypeDef] -> ExceptT ErrorTriplet m [TypeDef]
-mergeTypes ts1 []       = return ts1
-mergeTypes ts1 (t2:ts2) = do
-  ts1' <- mergeType ts1 t2
-  mergeTypes ts1' ts2
+mergeTypes = foldM mergeType
 
 -- | Merge a type definition into a list of type definitions, so long as it
 -- does not contain contradictory information.
@@ -310,13 +297,6 @@
 -- | Error: one of the variable DBs provided cannot be merged.
 ecCannotMergeVariableDB :: ErrorCode
 ecCannotMergeVariableDB = 1
-
--- | Merge two @Maybe@ values, prefering the left one if two @Just@s are
--- provided.
-mergeMaybe :: Maybe a -> Maybe a -> Maybe a
-mergeMaybe Nothing x       = x
-mergeMaybe x       Nothing = x
-mergeMaybe x       _       = x
 
 -- | Implement instances of parser to read variable DB from JSON, dropping the
 -- prefix in each field name.
diff --git a/src/Data/Aeson/Extra.hs b/src/Data/Aeson/Extra.hs
--- a/src/Data/Aeson/Extra.hs
+++ b/src/Data/Aeson/Extra.hs
@@ -28,7 +28,7 @@
 --
 -- Fails if the values are not objects or null.
 mergeObjects :: Value -> Value -> Value
-mergeObjects (Object m1) (Object m2) = Object (union m1 m2)
+mergeObjects (Object m1) (Object m2) = Object (m1 `union` m2)
 mergeObjects obj         Null        = obj
 mergeObjects Null        obj         = obj
 mergeObjects _           _           = error "The values passed are not objects"
diff --git a/src/Data/Diagram/Parser.hs b/src/Data/Diagram/Parser.hs
--- a/src/Data/Diagram/Parser.hs
+++ b/src/Data/Diagram/Parser.hs
@@ -64,7 +64,7 @@
            (\msg -> ErrorTriplet ecCannotReadDiagram msg (LocationFile fp))
            diagramE
 
---- | Generic function to parse a diagram.
+-- | Generic function to parse a diagram.
 parseDiagram :: DiagramFormat          -- ^ Format of the input file
              -> B.ByteString           -- ^ Contents of the diagram
              -> ExprPair               -- ^ Subparser for conditions or edge
diff --git a/src/Data/Diagram/Parser/Mermaid.hs b/src/Data/Diagram/Parser/Mermaid.hs
--- a/src/Data/Diagram/Parser/Mermaid.hs
+++ b/src/Data/Diagram/Parser/Mermaid.hs
@@ -79,6 +79,7 @@
 
   pure $ Diagram transitions
 
+{-# ANN pGraphTransition ("HLint: ignore Redundant bracket" :: String) #-}
 -- | Parser for an edge in a state diagram.
 --
 -- This parser depends on an auxiliary parser for the expressions associated to
@@ -109,6 +110,8 @@
 
   pure $ Diagram transitions
 
+{-# ANN pStateTransition ("HLint: ignore Redundant bracket" :: String)  #-}
+{-# ANN pStateTransition ("HLint: ignore Reduce duplication" :: String) #-}
 -- | Parser for transition label in stateDiagram-v2 mermaid diagram.
 pStateTransition :: ExprPair -> MermaidParser (Int, String, Int)
 pStateTransition ep@(ExprPair (ExprPairT { exprTParse = parseProp })) = do
@@ -129,7 +132,7 @@
 
   _ <- newline
 
-  pure $ (from, exprPairShow ep edge, to)
+  pure (from, exprPairShow ep edge, to)
 
 -- | Parser for Mermaid diagrams of type sequenceDiagram.
 pSequenceDiagram :: ExprPair -> MermaidParser Diagram
@@ -143,6 +146,7 @@
 
   pure $ Diagram transitions
 
+{-# ANN pSequenceTransition ("HLint: ignore Redundant bracket" :: String) #-}
 -- | Parser for a connection, message or transition in a sequence diagram.
 --
 -- This parser depends on an auxiliary parser for the expressions associated to
@@ -176,6 +180,6 @@
   , string "-)"
   ]
 
--- | Consume spaces
+-- | Consume spaces.
 spaces :: MermaidParser ()
 spaces = L.space space1 empty empty
diff --git a/src/Data/ExprPair.hs b/src/Data/ExprPair.hs
--- a/src/Data/ExprPair.hs
+++ b/src/Data/ExprPair.hs
@@ -71,14 +71,14 @@
 exprPair "lustre" = ExprPair $
   ExprPairT
     (Lustre.pBoolSpec . Lustre.myLexer)
-    (\_ -> id)
+    (const id)
     Lustre.boolSpec2Copilot
     Lustre.boolSpecNames
     (Lustre.BoolSpecSignal (Lustre.Ident "undefined"))
 exprPair "literal" = ExprPair $
   ExprPairT
     Right
-    (\_ -> id)
+    (const id)
     id
     (const [])
     "undefined"
diff --git a/src/Data/Project.hs b/src/Data/Project.hs
--- a/src/Data/Project.hs
+++ b/src/Data/Project.hs
@@ -17,15 +17,22 @@
 -- under the License.
 --
 -- | Ogma projects.
-module Data.Project where
+module Data.Project
+    ( Project(..)
+    , readProject
+    )
+  where
 
 -- External imports
 import           Control.Exception (IOException, try)
 import           Data.Aeson        (FromJSON, ToJSON, eitherDecodeStrict')
 import qualified Data.ByteString   as BS
+import           Data.List         (stripPrefix)
 import           GHC.Generics      (Generic)
+import           System.Directory  (makeAbsolute)
+import           System.FilePath   (isAbsolute, takeDirectory, (</>))
 
--- -- Internal imports
+-- Internal imports
 import Data.Either.Extra (mapLeft)
 
 data Project = Project
@@ -49,7 +56,49 @@
 readProject :: FilePath -> IO (Either String Project)
 readProject path = do
   bytesResult <- try (BS.readFile path)
-  pure $ case bytesResult of
-           Left e      -> Left (show (e :: IOException))
-           Right bytes -> mapLeft ("Failed to read project: " ++)
-                        $ eitherDecodeStrict' bytes
+  let project = case bytesResult of
+                  Left e      -> Left (show (e :: IOException))
+                  Right bytes -> mapLeft ("Failed to read project: " ++)
+                               $ eitherDecodeStrict' bytes
+
+  projectDir <- takeDirectory <$> makeAbsolute path
+
+  pure $ resolveProjectPaths projectDir <$> project
+
+-- | Resolve paths inside a project.
+resolveProjectPaths :: FilePath -> Project -> Project
+resolveProjectPaths projectDir p = p
+  { projectInputFiles     = resolveInputFile projectDir <$> projectInputFiles p
+  , projectVariableFiles  = resolvePath projectDir <$> projectVariableFiles p
+  , projectVariableDBFile = resolvePath projectDir <$> projectVariableDBFile p
+  , projectHandlerFile    = resolvePath projectDir <$> projectHandlerFile p
+  , projectCommandPropVia = resolvePath projectDir <$> projectCommandPropVia p
+  , projectTemplateDir    = resolvePath projectDir <$> projectTemplateDir p
+  , projectTargetDir      = resolvePath projectDir <$> projectTargetDir p
+  , projectExtraJSONFile  = resolvePath projectDir <$> projectExtraJSONFile p
+  }
+
+-- | Resolve the locations of input file pairs, possibly in relation to the
+-- project path.
+resolveInputFile :: FilePath
+                 -> (FilePath, String, String)
+                 -> (FilePath, String, String)
+resolveInputFile projectDir (filePath, fileFormat, propName) =
+    (filePath', fileFormat', propName)
+  where
+    filePath'   = resolvePath projectDir filePath
+    fileFormat' = resolvePath projectDir fileFormat
+
+-- | Resolve the path to a file, possibly in relation to the project path.
+resolvePath :: FilePath -> FilePath -> FilePath
+resolvePath projectDir path
+  | isAbsolute path
+  = path
+  | Just path' <- stripPrefix "cwd:" path
+  = path'
+  | Just path' <- stripPrefix "cmd:" path
+  = path'
+  | Just path' <- stripPrefix "project:" path
+  = projectDir </> path'
+  | otherwise
+  = projectDir </> path
diff --git a/src/Data/Spec/Analysis.hs b/src/Data/Spec/Analysis.hs
--- a/src/Data/Spec/Analysis.hs
+++ b/src/Data/Spec/Analysis.hs
@@ -243,7 +243,7 @@
       map ("       " ++) [ n ++ " :: " ++ t, n ++ " = " ++ i ]
 
     formatTrigger (_, _, h, g, a) =
-      map ("   " ++) [ "trigger " ++ show h ++ " (" ++ g ++ ") " ++ a ]
+      [ "   trigger " ++ show h ++ " (" ++ g ++ ") " ++ a ]
 
 -- | Default imports for a 'Spec' that was converted into a 'Copilot.Spec'.
 defaultSpecImports :: [(String, Maybe String)]
diff --git a/src/Data/Spec/Extra.hs b/src/Data/Spec/Extra.hs
--- a/src/Data/Spec/Extra.hs
+++ b/src/Data/Spec/Extra.hs
@@ -33,7 +33,7 @@
 addMissingIdentifiers f s = s { externalVariables = vars' }
   where
     vars'   = externalVariables s ++ newVars
-    newVars = map (\n -> ExternalVariableDef n "") newVarNames
+    newVars = map (`ExternalVariableDef` "") newVarNames
 
     -- Names that are not defined anywhere
     newVarNames = identifiers \\ existingNames
diff --git a/src/Data/Spec/Parser.hs b/src/Data/Spec/Parser.hs
--- a/src/Data/Spec/Parser.hs
+++ b/src/Data/Spec/Parser.hs
@@ -29,19 +29,16 @@
 import           Control.Monad.Except (ExceptT (..))
 import           Data.Aeson           (eitherDecode)
 import qualified Data.ByteString.Lazy as L
-import           Data.List            (isInfixOf, isPrefixOf, nub, (\\))
+import           Data.List            (isInfixOf, isPrefixOf)
 import           System.Directory     (doesFileExist)
 import           System.FilePath      ((</>))
 import           System.Process       (readProcess)
 
 -- External imports: auxiliary
 import Data.ByteString.Extra as B (safeReadFile)
-import Data.String.Extra     (sanitizeLCIdentifier, sanitizeUCIdentifier)
 
 -- External imports: ogma
-import Data.OgmaSpec            (ExternalVariableDef (..),
-                                 InternalVariableDef (..), Requirement (..),
-                                 Spec (..))
+import Data.OgmaSpec            (Requirement (..), Spec (..))
 import Language.CSVSpec.Parser  (parseCSVSpec)
 import Language.JSONSpec.Parser (parseJSONSpec)
 import Language.XLSXSpec.Parser (parseXLSXSpec)
@@ -50,7 +47,6 @@
 
 -- Internal imports: auxiliary
 import Command.Errors    (ErrorTriplet(..), ErrorCode)
-import Data.Diagram      (Diagram)
 import Data.Either.Extra (mapLeft)
 import Data.ExprPair     (ExprPairT(..))
 import Data.Location     (Location (..))
@@ -79,8 +75,8 @@
     -- Return the spec, transforming the error message if applicable.
     pure $ mapLeft (cannotReadConditionExpr expr) spec
 
---- | Process input specification, if available, and return its abstract
---- representation.
+-- | Process input specification, if available, and return its abstract
+-- representation.
 readInputFile :: FilePath
               -> String
               -> String
diff --git a/src/Language/Trans/CStruct2CopilotStruct.hs b/src/Language/Trans/CStruct2CopilotStruct.hs
--- a/src/Language/Trans/CStruct2CopilotStruct.hs
+++ b/src/Language/Trans/CStruct2CopilotStruct.hs
@@ -35,7 +35,7 @@
 -- Internal imports
 import qualified Language.C.AbsC as C
 
--- | Convert a top-level struct declaration into a CStruct
+-- | Convert a top-level struct declaration into a CStruct.
 mkCStruct :: C.ExternalDeclaration -> Either String CStruct
 mkCStruct (C.MkExternalDeclarationFunctionDefinition _) = Left "C files must contain struct definitions only."
 mkCStruct (C.MkExternalDeclarationDeclaration (C.MkDeclaration specifiers initDecl)) =
@@ -48,7 +48,7 @@
       in CStruct <$> name <*> fields
     _ -> Left "C files must contain struct definitions only."
 
--- -- | Convert a declaration within a struct into a field declaration.
+-- | Convert a declaration within a struct into a field declaration.
 buildCField :: C.StructDeclaration -> Either String CField
 buildCField (C.MkStructDeclaration field name)
     | fieldLength > 0 = CArray <$> fieldType <*> fieldName <*> pure fieldLength
@@ -77,7 +77,7 @@
 showTypeSpecifier C.MkTypeSpecifierInt64  = "int64_t"
 showTypeSpecifier C.MkTypeSpecifierInt    = "int"
 
--- -- | Extract the name of a field from a struct declarator.
+-- | Extract the name of a field from a struct declarator.
 extractFieldName :: Read n => C.StructDeclarator -> Either String n
 extractFieldName (C.MkStructDeclaratorDeclarator (C.MkDeclarator C.MkPointerOptNothing (C.MkDirectDeclaratorIdentifier (C.Identifier d)))) = Right $ read $ show d
 extractFieldName (C.MkStructDeclaratorDeclarator
@@ -92,8 +92,8 @@
 extractFieldName _ = Left $ "only struct declarations that are IDs without a"
                         ++  " pointer, or plain arrays without a pointer, are"
                         ++  " supported."
---
--- -- | Extract the length of an array field from a struct declarator.
+
+-- | Extract the length of an array field from a struct declarator.
 extractFieldLength :: C.StructDeclarator -> Integer
 extractFieldLength (C.MkStructDeclaratorDeclarator
                      (C.MkDeclarator
@@ -139,7 +139,6 @@
                    ) = read i
 extractFieldLength _ = 0
 
---
 -- | Convert a 'String' to camel case, also eliminating the @_t@ at the end if
 -- present.
 camelCaseTypeName :: String -> String
diff --git a/src/Language/Trans/CStructs2Copilot.hs b/src/Language/Trans/CStructs2Copilot.hs
--- a/src/Language/Trans/CStructs2Copilot.hs
+++ b/src/Language/Trans/CStructs2Copilot.hs
@@ -28,7 +28,6 @@
 
 -- External imports
 import Data.Char ( isUpper, toLower )
-import Data.List ( intercalate )
 
 -- External imports: auxiliary
 import Data.List.Extra ( toHead, toTail )
diff --git a/src/Language/Trans/Spec2Copilot.hs b/src/Language/Trans/Spec2Copilot.hs
--- a/src/Language/Trans/Spec2Copilot.hs
+++ b/src/Language/Trans/Spec2Copilot.hs
@@ -140,31 +140,12 @@
                 (Just ty, Just ex) -> "[ arg (" ++ showExpr ex ++ " ) ]"
                 _                  -> "[]"
 
-    -- Map from a variable name to its desired identifier in the code
-    -- generated.
-    internalVariableMap =
-      map (\x -> (x, sanitizeLCIdentifier x)) internalVariableNames
-
-    externalVariableMap =
-      map (\x -> (x, sanitizeLCIdentifier x)) externalVariableNames
-
-    requirementNameMap =
-      map (\x -> (x, "prop" ++ sanitizeUCIdentifier x)) requirementNames
-
-    nameSubstitutions = internalVariableMap
-                     ++ externalVariableMap
-                     ++ requirementNameMap
-
-    -- Variable/requirement names used in the input spec.
-    internalVariableNames = map internalVariableName
-                          $ internalVariables spec
-
-    externalVariableNames = map externalVariableName
-                          $ externalVariables spec
-
-    requirementNames = map requirementName
-                     $ requirements spec
+    nameSubstitutions = internalVariableMap spec
+                     ++ externalVariableMap spec
+                     ++ requirementNameMap spec
 
+-- | Check that a specification does not contain any name clashes between
+-- variables and/or requirements.
 specAnalyze :: Spec a -> Either String (Spec a)
 specAnalyze spec
     | not (null evnClash)
@@ -193,30 +174,9 @@
                                 `union` externalVariableNames')
 
     -- Names used.
-    internalVariableNames' = map snd internalVariableMap
-    externalVariableNames' = map snd externalVariableMap
-    requirementNames'      = map snd requirementNameMap
-
-    -- Map from a variable name to its desired identifier in the code
-    -- generated.
-    internalVariableMap =
-      map (\x -> (x, sanitizeLCIdentifier x)) internalVariableNames
-
-    externalVariableMap =
-      map (\x -> (x, sanitizeLCIdentifier x)) externalVariableNames
-
-    requirementNameMap =
-      map (\x -> (x, "prop" ++ sanitizeUCIdentifier x)) requirementNames
-
-    -- Variable/requirement names used in the input spec.
-    internalVariableNames = map internalVariableName
-                          $ internalVariables spec
-
-    externalVariableNames = map externalVariableName
-                          $ externalVariables spec
-
-    requirementNames = map requirementName
-                     $ requirements spec
+    internalVariableNames' = map snd (internalVariableMap spec)
+    externalVariableNames' = map snd (externalVariableMap spec)
+    requirementNames'      = map snd (requirementNameMap spec)
 
 -- * Auxiliary
 
@@ -232,3 +192,33 @@
 -- an end of line character at the end of the last string.
 unlines' :: [String] -> String
 unlines' = intercalate "\n"
+
+-- | Map from an internal variable name to its desired identifier in the code
+-- generated.
+internalVariableMap :: Spec a -> [(String, String)]
+internalVariableMap =
+  map (\x -> (x, sanitizeLCIdentifier x)) . internalVariableNames
+
+-- | Map from an external variable name to its desired identifier in the code
+-- generated.
+externalVariableMap :: Spec a -> [(String, String)]
+externalVariableMap =
+  map (\x -> (x, sanitizeLCIdentifier x)) . externalVariableNames
+
+-- | Map from a requirement name to its desired identifier in the code
+-- generated.
+requirementNameMap :: Spec a -> [(String, String)]
+requirementNameMap =
+  map (\x -> (x, "prop" ++ sanitizeUCIdentifier x)) . requirementNames
+
+-- | Names of all internal variables in a 'Spec'.
+internalVariableNames :: Spec a -> [String]
+internalVariableNames = map internalVariableName . internalVariables
+
+-- | Names of all external variables in a 'Spec'.
+externalVariableNames :: Spec a -> [String]
+externalVariableNames = map externalVariableName . externalVariables
+
+-- | Names of all requirements in a 'Spec'.
+requirementNames :: Spec a -> [String]
+requirementNames = map requirementName . requirements
diff --git a/src/Language/YAMLSpec/Parser.hs b/src/Language/YAMLSpec/Parser.hs
--- a/src/Language/YAMLSpec/Parser.hs
+++ b/src/Language/YAMLSpec/Parser.hs
@@ -20,19 +20,19 @@
 module Language.YAMLSpec.Parser where
 
 -- External imports
-import           Control.Monad.Except   (ExceptT (..), runExceptT)
-import           Control.Monad.IO.Class (liftIO)
-import           Data.Aeson             (Value (..))
-import           Data.Aeson.Key         (fromString)
-import qualified Data.Aeson.KeyMap      as M
-import           Data.Bifunctor         (first)
-import qualified Data.ByteString        as BS
-import           Data.Char              (isSpace)
-import           Data.List              (intercalate)
-import           Data.Text              (unpack)
-import qualified Data.Vector            as V
-import qualified Data.Yaml              as Y
-import           System.FilePath        (takeBaseName, takeFileName)
+import           Control.Monad.Except (ExceptT (..), runExceptT)
+import           Data.Aeson           (Value (..))
+import           Data.Aeson.Key       (fromString)
+import qualified Data.Aeson.KeyMap    as M
+import           Data.Bifunctor       (first)
+import qualified Data.ByteString      as BS
+import           Data.Char            (isSpace)
+import           Data.List            (intercalate)
+import           Data.Maybe           (maybeToList)
+import           Data.Text            (unpack)
+import qualified Data.Vector          as V
+import qualified Data.Yaml            as Y
+import           System.FilePath      (takeBaseName, takeFileName)
 
 -- External imports: ogma-spec
 import Data.Either.Extra (mapLeft)
@@ -249,7 +249,7 @@
 --
 -- If the values are an array, it returns the values in the array directly.
 objectFieldValues :: String -> Value -> [Value]
-objectFieldValues key (Object o) = maybe [] (:[]) $ M.lookup (fromString key) o
+objectFieldValues key (Object o) = maybeToList $ M.lookup (fromString key) o
 objectFieldValues _   _          = []
 
 -- ** Either-related auxiliary functions
diff --git a/templates/fprime/Dockerfile b/templates/fprime/Dockerfile
--- a/templates/fprime/Dockerfile
+++ b/templates/fprime/Dockerfile
@@ -1,11 +1,11 @@
-# This dockerfile compiles a monitoring application inside FPrime's Reference
+# This dockerfile compiles a monitoring application inside F Prime's Reference
 # Application.
 FROM ubuntu:focal
 
 # Avoid questions during package installation.
 ENV DEBIAN_FRONTEND=noninteractive
 
-# Install FPrime dependencies and clone fprime from the repo.
+# Install F Prime dependencies and clone fprime from the repo.
 RUN apt-get update
 RUN apt-get install -y git cmake gcc python3 pip default-jre
 
diff --git a/templates/ros/Dockerfile b/templates/ros/Dockerfile
--- a/templates/ros/Dockerfile
+++ b/templates/ros/Dockerfile
@@ -1,4 +1,9 @@
-FROM osrf/space-ros:jazzy-2026.04.0
+{{#BASE_DOCKER_IMAGE}}
+FROM {{.}}
+{{/BASE_DOCKER_IMAGE}}
+{{^BASE_DOCKER_IMAGE}}
+FROM osrf/space-ros:jazzy-2026.07.0
+{{/BASE_DOCKER_IMAGE}}
 
 ARG USER=spaceros-user
 ARG PACKAGE_PATH=/home/${USER}/monitors
@@ -26,7 +31,7 @@
     fi
 
 ADD excluded-pkgs*.txt /tmp/
-RUN sudo rosdep init
+RUN if [ ! -f "/etc/ros/rosdep/sources.list.d/20-default.list" ]; then sudo rosdep init; fi
 RUN rosdep update
 RUN source /opt/ros/spaceros/setup.bash && \
     if [ -f "/tmp/excluded-pkgs.txt" ]; then \
diff --git a/templates/ros/copilot/src/copilot_monitor.cpp b/templates/ros/copilot/src/copilot_monitor.cpp
--- a/templates/ros/copilot/src/copilot_monitor.cpp
+++ b/templates/ros/copilot/src/copilot_monitor.cpp
@@ -77,8 +77,8 @@
 
 {{/monitors}}
     // Needed so we can report messages to the log.
-    static CopilotRV& getInstance() {
-      static CopilotRV instance;
+    static std::shared_ptr<CopilotRV> getInstance() {
+      static std::shared_ptr<CopilotRV> instance = std::shared_ptr<CopilotRV>(new CopilotRV());
       return instance;
     }
 
@@ -115,19 +115,19 @@
 // communicate with other applications.
 {{#monitorType}}
 void {{monitorName}}({{.}} arg) {
-  CopilotRV::getInstance().{{monitorName}}(arg);
+  CopilotRV::getInstance()->{{monitorName}}(arg);
 }
 {{/monitorType}}
 {{^monitorType}}
 void {{monitorName}}() {
-  CopilotRV::getInstance().{{monitorName}}();
+  CopilotRV::getInstance()->{{monitorName}}();
 }
 {{/monitorType}}
 
 {{/monitors}}
 int main(int argc, char* argv[]) {
   rclcpp::init(argc, argv);
-  rclcpp::spin(std::make_shared<CopilotRV>());
+  rclcpp::spin(CopilotRV::getInstance());
   rclcpp::shutdown();
   return 0;
 }
diff --git a/templates/ros/test_requirements/src/test_requirements.cpp b/templates/ros/test_requirements/src/test_requirements.cpp
--- a/templates/ros/test_requirements/src/test_requirements.cpp
+++ b/templates/ros/test_requirements/src/test_requirements.cpp
@@ -113,10 +113,10 @@
 
 {{#testingVariables}}
        {{#varDeclMsgField}}
-       {{varDeclType}} {{varDeclName}}_{{.}} = {{varDeclRandom}}();
-       auto {{varDeclName}}_{{.}}_msg = {{varDeclMsgType}}();
-       {{varDeclName}}_{{.}}_msg.{{.}} = {{varDeclName}}_{{.}};
-       {{varDeclName}}_publisher_->publish({{varDeclName}}_{{.}}_msg);
+       {{varDeclType}} {{varDeclName}}_data = {{varDeclRandom}}();
+       auto {{varDeclName}}_data_msg = {{varDeclMsgType}}();
+       {{varDeclName}}_data_msg.{{.}} = {{varDeclName}}_data;
+       {{varDeclName}}_publisher_->publish({{varDeclName}}_data_msg);
        {{/varDeclMsgField}}
        {{^varDeclMsgField}}
        {{varDeclType}} {{varDeclName}}_data = {{varDeclRandom}}();
diff --git a/tests/Main.hs b/tests/Main.hs
--- a/tests/Main.hs
+++ b/tests/Main.hs
@@ -1,4 +1,5 @@
--- | Test ogma-core
+{- HLint ignore "Reduce duplication" -}
+-- | Test ogma-core.
 module Main where
 
 import Data.Monoid                    ( mempty )
@@ -14,8 +15,7 @@
 
 -- | Run all unit tests on ogma-core.
 main :: IO ()
-main =
-  defaultMainWithOpts tests mempty
+main = defaultMainWithOpts tests mempty
 
 -- | All unit tests for ogma-core.
 tests :: [Test.Framework.Test]
