Repository: exercism/clojure
Pull request #681 (2024-10-05 11:12:52)

rna-transcription: Implement tests
Implementing tests to address the problem described here:

https://forum.exercism.org/t/confusing-message-about-a-test-failure-while-still-accepting-solution/13254

github-actions[bot]: This PR touches files which potentially affect the outcome of the tests of an exercise. This will cause all students' solutions to affected exercises to be re-tested.

If this PR does **not** affect the result of the test (or, for example, adds an edge case that is not worth rerunning all tests for), **please add the following to the merge-commit message** which will stops student's tests from re-running. Please copy-paste to avoid typos.
```
[no important files changed]
```

 For more information, refer to the [documentation](https://exercism.org/docs/building/tracks#h-avoiding-triggering-unnecessary-test-runs). If you are unsure whether to add the message or not, please ping `@exercism/maintainers-admin` in a comment. Thank you!

Patch:
diff --git a/_src/clock_generator.clj b/_generators/clock_generator.clj
similarity index 100%
rename from _src/clock_generator.clj
rename to _generators/clock_generator.clj
diff --git a/_src/generator.clj b/_generators/generator.clj
similarity index 100%
rename from _src/generator.clj
rename to _generators/generator.clj
diff --git a/_generators/list-ops-generator.clj b/_generators/list-ops-generator.clj
new file mode 100644
index 0000000..59f7213
--- /dev/null
+++ b/_generators/list-ops-generator.clj
@@ -0,0 +1,177 @@
+#!/usr/bin/env bb
+
+(require '[cheshire.core :as json]
+         '[babashka.fs :as fs]
+         '[clojure.string :as str]
+         '[clojure.edn :as edn])
+
+(comment
+  (def slug "list-ops"))
+
+(def data
+  (let [url "https://raw.githubusercontent.com/exercism/problem-specifications/main/exercises/"]
+    {:canonical-data (json/parse-string (slurp (str url "/" slug "/canonical-data.json")) true)
+     :description (slurp (str url "/" slug "/description.md"))
+     :metadata (slurp (str url "/" slug "/metadata.toml"))}))
+
+(second
+ (str/split (:metadata data) #"="))
+
+(defn get-meta 
+  "Returns a vector containing the exercise title and blurb"
+  [data]
+  (mapv last
+       (map #(map str/trim (str/split % #"="))
+            (str/split-lines (:metadata data)))))
+
+(defn init-deps! [data]
+  (fs/create-dirs (fs/path "exercises" "practice"
+                           (:exercise (:canonical-data data)) "src"))
+  (spit (str (fs/file "exercises" "practice"
+                      (:exercise (:canonical-data data))
+                      "deps.edn"))
+        "{:aliases {:test {:extra-paths [\"test\"]
+                 :extra-deps {io.github.cognitect-labs/test-runner 
+                              {:git/url \"https://github.com/cognitect-labs/test-runner.git\"
+                               :sha \"705ad25bbf0228b1c38d0244a36001c2987d7337\"}}
+                 :main-opts [\"-m\" \"cognitect.test-runner\"]
+                 :exec-fn cognitect.test-runner.api/test}}}"))
+
+(comment
+  (init-deps! data)
+  )
+
+(defn init-lein! [data]
+  (let [slug (:exercise (:canonical-data data))]
+    (spit (str (fs/file "exercises" "practice"
+                        (:exercise (:canonical-data data)) "project.clj"))
+          (str "(defproject " slug " \"0.1.0-SNAPSHOT\"
+  :description \"" slug " exercise.\"
+  :url \"https://github.com/exercism/clojure/tree/main/exercises/" slug "\"
+  :dependencies [[org.clojure/clojure \"1.10.0\"]])
+"))))
+
+(comment
+  (init-lein! data)
+  )
+
+(defn test-ns-form [data]
+  (str "(ns " (:exercise data) "-test
+  (:require [clojure.test :refer [deftest testing is]]\n             "
+       (:exercise data) "))\n\n"))
+
+(defn src-ns-form [data]
+  (str "(ns " (:exercise data) ")\n\n"))
+
+(defn trans-fn [s]
+  (let [[args body] (str/split s #"->")
+        arg-strs (mapv str (edn/read-string args))
+        [arg1 op arg2] (str/split (str/trim body) #"\s")]
+    (str "(fn [" (apply str (interpose " " arg-strs)) "] " 
+         "(" op " " arg1 " " arg2 "))")))
+
+(comment
+  (trans-fn "(x) -> x + 1")  
+  (trans-fn "(x, y) -> x * y")
+  (trans-fn "(acc, el) -> el * acc")
+  )
+
+(defn testing-form [slug test-case]
+  (let [property (symbol (str slug "/" (:property test-case)))
+        input (:input test-case)
+        args (map #(get input %) (keys input))]
+    (str "  (testing \"" (:description test-case) "\"
+     (is (= " (:expected test-case) " "
+         (reverse (into (list property) args)) ")))")))
+
+(comment
+  (testing-form "list-ops" (first (:cases (first (:cases (:canonical-data data))))))
+  )
+
+(defn testing-forms
+  "Outputs a sequence of the test cases for a given property name
+   given its name as a string and the canonical data."
+  [property data]
+  (let [test-cases (filter #(= property (:property %))
+                           (mapcat :cases
+                                   (:cases (:canonical-data data))))]
+    (map #(testing-form (:exercise (:canonical-data data)) %) test-cases)))
+
+(comment
+  (testing-forms "append" data)
+  )
+
+(defn deftest-forms [data]
+  (for [property (distinct (map :property (mapcat :cases
+                                                  (:cases (:canonical-data data)))))]
+    (str "(deftest " property "-test\n"
+         (apply str (interpose "\n"
+                               (testing-forms property data)))
+         ")")))
+
+(comment
+  (deftest-forms data)
+  )
+
+(defn init-tests! [data]
+  (let [path (fs/path "exercises" "practice"
+                      (:exercise (:canonical-data data)) "test")]
+    (when-not (fs/directory? path)
+      (fs/create-dir path))
+    (spit (str (fs/file "exercises" "practice"
+                        (:exercise (:canonical-data data)) "test"
+                        (str (str/replace (:exercise (:canonical-data data)) "-" "_")
+                             "_test.clj")))
+          (str (test-ns-form (:canonical-data data))
+               (apply str (interpose "\n\n"
+                                     (deftest-forms data)))))))
+
+(comment
+  (init-tests! data)
+  )
+
+(defn init-src! [data]
+  (spit (str (fs/file "exercises" "practice" (:exercise (:canonical-data data)) "src"
+                      (str (str/replace (:exercise (:canonical-data data))
+                                        "-" "_") ".clj")))
+        (str (src-ns-form (:canonical-data data))
+             (apply str (interpose "\n\n"
+                                   (for [property (distinct (map :property (mapcat :cases
+                                                                                   (:cases (:canonical-data data)))))]
+                                     (str "(defn " property " []\n  )")))))))
+
+(comment
+  (init-src! data)
+  )
+
+(defn init-description! [data]
+  (let [path ["exercises" "practice" (:exercise (:canonical-data data)) ".docs"]]
+    (when-not (fs/directory? (apply fs/path path))
+      (fs/create-dir (apply fs/path path))
+      (spit (str (apply fs/file (conj path "instructions.md")))
+            (:description data)))))
+
+(comment
+  (init-description! data)
+  )
+
+(defn config [data author blurb]
+  (let [slug (:exercise (:canonical-data data))]
+    {:authors [author],
+     :contributors [],
+     :files {:solution [(str "src/" (str/replace slug "-" "_") ".clj")], 
+             :test [(str "test/" (str/replace slug "-" "_") "_test.clj")], 
+             :example [".meta/src/example.clj"]},
+     :blurb blurb}))
+
+(defn init-config! [data]
+  (let [path ["exercises" "practice" (:exercise (:canonical-data data)) ".meta"]]
+    (when-not (fs/directory? (apply fs/path path))
+   (fs/create-dirs (apply fs/path (conj path "src")))
+      (spit (str (apply fs/file (conj path "config.json")))
+            (json/generate-string (config data "porkostomus" (last (get-meta data)))
+                                  {:pretty true})))))
+
+(comment
+  (init-config! data)
+  )
\ No newline at end of file
diff --git a/generator.clj b/_generators/zipper-generator.clj
similarity index 96%
rename from generator.clj
rename to _generators/zipper-generator.clj
index 3c97c75..6ac5848 100644
--- a/generator.clj
+++ b/_generators/zipper-generator.clj
@@ -16,12 +16,12 @@
 (second
  (str/split (:metadata data) #"="))
 
-(defn get-meta 
+(defn get-meta
   "Returns a vector containing the exercise title and blurb"
   [data]
   (mapv last
-       (map #(map str/trim (str/split % #"="))
-            (str/split-lines (:metadata data)))))
+        (map #(map str/trim (str/split % #"="))
+             (str/split-lines (:metadata data)))))
 
 (defn init-deps [data]
   (fs/create-dirs (fs/path "exercises" "practice"
@@ -122,19 +122,18 @@
   (let [slug (:exercise (:canonical-data data))]
     {:authors [author],
      :contributors [],
-     :files {:solution [(str "src/" (str/replace slug "-" "_") ".clj")], 
-             :test [(str "test/" (str/replace slug "-" "_") "_test.clj")], 
+     :files {:solution [(str "src/" (str/replace slug "-" "_") ".clj")],
+             :test [(str "test/" (str/replace slug "-" "_") "_test.clj")],
              :example [".meta/src/example.clj"]},
      :blurb blurb}))
 
 (defn init-config! [data]
   (let [path ["exercises" "practice" (:exercise (:canonical-data data)) ".meta"]]
     (when-not (fs/directory? (apply fs/path path))
-   (fs/create-dirs (apply fs/path (conj path "src")))
+      (fs/create-dirs (apply fs/path (conj path "src")))
       (spit (str (apply fs/file (conj path "config.json")))
             (json/generate-string (config data "porkostomus" (last (get-meta data)))
                                   {:pretty true})))))
 
 (comment
-  (init-config! data)
-  )
\ No newline at end of file
+  (init-config! data))
\ No newline at end of file
diff --git a/config.json b/config.json
index 698bace..aacb72e 100644
--- a/config.json
+++ b/config.json
@@ -26,7 +26,7 @@
       "test/%{snake_slug}_test.clj"
     ],
     "example": [
-      "src/example.clj"
+      ".meta/src/example.clj"
     ],
     "exemplar": [
       ".meta/exemplar.clj"
@@ -122,7 +122,9 @@
           "sequential-destructuring"
         ],
         "prerequisites": [
-          "lists", "vectors", "strings"
+          "lists",
+          "vectors",
+          "strings"
         ],
         "status": "beta"
       },
@@ -138,8 +140,13 @@
         "slug": "squeaky-clean",
         "name": "Squeaky Clean",
         "uuid": "3af5b219-d751-4243-89f7-c8f05216f534",
-        "concepts": ["chars"],
-        "prerequisites": ["basics", "strings"],
+        "concepts": [
+          "chars"
+        ],
+        "prerequisites": [
+          "basics",
+          "strings"
+        ],
         "status": "beta"
       },
       {
@@ -154,16 +161,27 @@
         "slug": "coordinate-transformation",
         "name": "Coordinate Transformation",
         "uuid": "5b23eabd-6ebb-4fed-b889-9324ad174a5f",
-        "concepts": ["closures", "atoms"],
-        "prerequisites": ["vectors"],
+        "concepts": [
+          "closures",
+          "atoms"
+        ],
+        "prerequisites": [
+          "vectors"
+        ],
         "status": "beta"
       },
       {
         "slug": "card-games",
         "name": "Card Games",
         "uuid": "01a06d2b-ffb1-4497-9bb3-d93ee2d0931b",
-        "concepts": ["lists"],
-        "prerequisites": ["basics", "conditionals", "booleans"],
+        "concepts": [
+          "lists"
+        ],
+        "prerequisites": [
+          "basics",
+          "conditionals",
+          "booleans"
+        ],
         "status": "beta"
       }
     ],
@@ -172,17 +190,24 @@
         "slug": "two-fer",
         "name": "Two Fer",
         "uuid": "1706ec00-9e51-45d1-ac3e-01b0360ea950",
-        "practices": ["strings"],
-        "prerequisites": ["strings"],
-        "difficulty": 1,
-        "topics": null
+        "practices": [
+          "strings"
+        ],
+        "prerequisites": [
+          "strings"
+        ],
+        "difficulty": 1
       },
       {
         "slug": "armstrong-numbers",
         "name": "Armstrong Numbers",
         "uuid": "0e0fec77-651a-45a7-af99-a768ebebef05",
-        "practices": ["numbers"],
-        "prerequisites": ["numbers"],
+        "practices": [
+          "numbers"
+        ],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 1,
         "topics": [
           "math"
@@ -204,10 +229,13 @@
         "name": "Reverse String",
         "uuid": "0f7b1858-15d0-467d-9f94-56408249c2f7",
         "practices": [],
-        "prerequisites": ["strings"],
+        "prerequisites": [
+          "strings"
+        ],
         "difficulty": 1,
         "topics": [
-          "strings", "core_functions"
+          "strings",
+          "core_functions"
         ]
       },
       {
@@ -215,7 +243,9 @@
         "name": "Accumulate",
         "uuid": "49f62bbc-0f60-4922-b5a6-f266b80442f4",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 2,
         "topics": [
           "algorithms, core_functions"
@@ -225,19 +255,30 @@
         "slug": "acronym",
         "name": "Acronym",
         "uuid": "b99ed7da-98a8-43f0-8162-05c5408f6ac5",
-        "practices": ["strings"],
-        "prerequisites": ["strings", "booleans"],
+        "practices": [
+          "strings"
+        ],
+        "prerequisites": [
+          "strings",
+          "booleans"
+        ],
         "difficulty": 2,
         "topics": [
-          "strings", "regular_expressions"
+          "strings",
+          "regular_expressions"
         ]
       },
       {
         "slug": "all-your-base",
         "name": "All Your Base",
         "uuid": "7b8c5f7e-3c7d-4e69-ba8c-f29bb492a628",
-        "practices": ["numbers"],
-        "prerequisites": ["numbers", "vectors"],
+        "practices": [
+          "numbers"
+        ],
+        "prerequisites": [
+          "numbers",
+          "vectors"
+        ],
         "difficulty": 2,
         "topics": [
           "math"
@@ -247,8 +288,13 @@
         "slug": "anagram",
         "name": "Anagram",
         "uuid": "11818959-7e42-4a1d-8dfd-288cea744d66",
-        "practices": ["strings"],
-        "prerequisites": ["strings", "vectors"],
+        "practices": [
+          "strings"
+        ],
+        "prerequisites": [
+          "strings",
+          "vectors"
+        ],
         "difficulty": 2,
         "topics": [
           "strings"
@@ -258,19 +304,31 @@
         "slug": "bob",
         "name": "Bob",
         "uuid": "0317cc62-dd75-4841-86c3-01c055630fe7",
-        "practices": ["conditionals"],
-        "prerequisites": ["conditionals", "strings"],
+        "practices": [
+          "conditionals"
+        ],
+        "prerequisites": [
+          "conditionals",
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "strings", "regular_expressions", "conditionals"
+          "strings",
+          "regular_expressions",
+          "conditionals"
         ]
       },
       {
         "slug": "collatz-conjecture",
         "name": "Collatz Conjecture",
         "uuid": "9516323c-721e-4bfc-84df-9bc32e467197",
-        "practices": ["numbers"],
-        "prerequisites": ["numbers", "conditionals"],
+        "practices": [
+          "numbers"
+        ],
+        "prerequisites": [
+          "numbers",
+          "conditionals"
+        ],
         "difficulty": 2,
         "topics": [
           "math"
@@ -280,8 +338,12 @@
         "slug": "complex-numbers",
         "name": "Complex Numbers",
         "uuid": "ceaacb97-9c2c-43db-a9ac-28ebe6650f47",
-        "practices": ["numbers"],
-        "prerequisites": ["numbers"],
+        "practices": [
+          "numbers"
+        ],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 2,
         "topics": [
           "math"
@@ -292,10 +354,15 @@
         "name": "ETL",
         "uuid": "befca7ee-fc29-4ef0-afd7-2d3f68aa47fb",
         "practices": [],
-        "prerequisites": ["lists", "numbers", "strings"],
+        "prerequisites": [
+          "lists",
+          "numbers",
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "integers", "maps"
+          "integers",
+          "maps"
         ]
       },
       {
@@ -303,18 +370,27 @@
         "name": "Hamming",
         "uuid": "d120ad9a-98e9-4de7-9cb6-6aef3101cd1c",
         "practices": [],
-        "prerequisites": ["numbers", "strings", "conditionals"],
+        "prerequisites": [
+          "numbers",
+          "strings",
+          "conditionals"
+        ],
         "difficulty": 2,
         "topics": [
-          "strings", "conditionals"
+          "strings",
+          "conditionals"
         ]
       },
       {
         "slug": "nth-prime",
         "name": "Nth Prime",
         "uuid": "167113f2-b015-4c04-809d-670193758c25",
-        "practices": ["numbers"],
-        "prerequisites": ["numbers"],
+        "practices": [
+          "numbers"
+        ],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 2,
         "topics": [
           "math"
@@ -325,40 +401,61 @@
         "name": "Nucleotide Count",
         "uuid": "223d10d6-539d-441f-89ba-77f7743e6092",
         "practices": [],
-        "prerequisites": ["strings", "conditionals"],
+        "prerequisites": [
+          "strings",
+          "conditionals"
+        ],
         "difficulty": 2,
         "topics": [
-          "strings", "conditionals", "exception_handling"
+          "strings",
+          "conditionals",
+          "exception_handling"
         ]
       },
       {
         "slug": "pangram",
         "name": "Pangram",
         "uuid": "1f96161b-c83a-4f66-bc50-3e32e035da1f",
-        "practices": ["strings"],
-        "prerequisites": ["strings", "booleans"],
+        "practices": [
+          "strings"
+        ],
+        "prerequisites": [
+          "strings",
+          "booleans"
+        ],
         "difficulty": 2,
         "topics": [
-          "strings", "maps"
+          "strings",
+          "maps"
         ]
       },
       {
         "slug": "pig-latin",
         "name": "Pig Latin",
         "uuid": "a9b08dfd-3555-46bc-bc54-d605fd225c34",
-        "practices": ["strings"],
-        "prerequisites": ["strings"],
+        "practices": [
+          "strings"
+        ],
+        "prerequisites": [
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "strings", "regular_expressions"
+          "strings",
+          "regular_expressions"
         ]
       },
       {
         "slug": "protein-translation",
         "name": "Protein Translation",
         "uuid": "591c3cc5-0d14-400d-85a2-4ce0e399813e",
-        "practices": ["strings"],
-        "prerequisites": ["strings", "vectors"],
+        "practices": [
+          "strings"
+        ],
+        "prerequisites": [
+          "strings",
+          "vectors"
+        ],
         "difficulty": 2,
         "topics": [
           "pattern_matching",
@@ -369,11 +466,19 @@
         "slug": "raindrops",
         "name": "Raindrops",
         "uuid": "e670c978-8b71-4880-a543-3f9fb28d88b3",
-        "practices": ["conditionals"],
-        "prerequisites": ["numbers", "strings", "conditionals"],
+        "practices": [
+          "conditionals"
+        ],
+        "prerequisites": [
+          "numbers",
+          "strings",
+          "conditionals"
+        ],
         "difficulty": 2,
         "topics": [
-          "integers", "strings", "conditionals"
+          "integers",
+          "strings",
+          "conditionals"
         ]
       },
       {
@@ -381,10 +486,13 @@
         "name": "RNA Transcription",
         "uuid": "d8773153-d717-43f1-9324-c7e8cf00455c",
         "practices": [],
-        "prerequisites": ["strings"],
+        "prerequisites": [
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "strings", "pattern_matching"
+          "strings",
+          "pattern_matching"
         ]
       },
       {
@@ -392,10 +500,16 @@
         "name": "Robot Name",
         "uuid": "ce7df719-d3e2-444f-8ae3-f81c89fd15ce",
         "practices": [],
-        "prerequisites": ["numbers", "strings"],
+        "prerequisites": [
+          "numbers",
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "sets", "conditionals", "regular_expressions", "randomness"
+          "sets",
+          "conditionals",
+          "regular_expressions",
+          "randomness"
         ]
       },
       {
@@ -403,7 +517,10 @@
         "name": "Roman Numerals",
         "uuid": "cad43fab-50c2-470e-8364-6592e971d4df",
         "practices": [],
-        "prerequisites": ["numbers", "strings"],
+        "prerequisites": [
+          "numbers",
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
           "conditionals"
@@ -414,10 +531,14 @@
         "name": "Rotational Cipher",
         "uuid": "a40e8d4b-9793-4991-87d8-7efb92a3e3f2",
         "practices": [],
-        "prerequisites": ["numbers", "strings"],
+        "prerequisites": [
+          "numbers",
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "algorithms", "regular_expressions"
+          "algorithms",
+          "regular_expressions"
         ]
       },
       {
@@ -425,10 +546,15 @@
         "name": "Run-Length Encoding",
         "uuid": "fa1be98f-59b2-487e-948d-46e744d50dc4",
         "practices": [],
-        "prerequisites": ["numbers", "strings"],
+        "prerequisites": [
+          "numbers",
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "strings", "regular_expressions", "parsing"
+          "strings",
+          "regular_expressions",
+          "parsing"
         ]
       },
       {
@@ -436,10 +562,14 @@
         "name": "Scrabble Score",
         "uuid": "2804d2d1-0029-4473-8431-0857d4767d91",
         "practices": [],
-        "prerequisites": ["numbers", "strings"],
+        "prerequisites": [
+          "numbers",
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "maps", "strings"
+          "maps",
+          "strings"
         ]
       },
       {
@@ -447,10 +577,15 @@
         "name": "Secret Handshake",
         "uuid": "9c270d52-b1fe-4f1a-a639-2f7347c7cb92",
         "practices": [],
-        "prerequisites": ["numbers", "vectors", "strings"],
+        "prerequisites": [
+          "numbers",
+          "vectors",
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "algorithms", "conditionals"
+          "algorithms",
+          "conditionals"
         ]
       },
       {
@@ -458,7 +593,10 @@
         "name": "Series",
         "uuid": "f342d3f2-775f-4fb6-93c2-9aea4a8f5f22",
         "practices": [],
-        "prerequisites": ["numbers", "vectors"],
+        "prerequisites": [
+          "numbers",
+          "vectors"
+        ],
         "difficulty": 2,
         "topics": [
           "strings"
@@ -468,8 +606,13 @@
         "slug": "space-age",
         "name": "Space Age",
         "uuid": "c97868e9-36ca-4d00-9483-0fc6b4c37231",
-        "practices": ["floating-point-numbers"],
-        "prerequisites": ["numbers", "floating-point-numbers"],
+        "practices": [
+          "floating-point-numbers"
+        ],
+        "prerequisites": [
+          "numbers",
+          "floating-point-numbers"
+        ],
         "difficulty": 2,
         "topics": [
           "math"
@@ -480,18 +623,27 @@
         "name": "Strain",
         "uuid": "8db4904c-a46f-4886-b214-0cf06b0f2ff1",
         "practices": [],
-        "prerequisites": ["conditionals", "numbers"],
+        "prerequisites": [
+          "conditionals",
+          "numbers"
+        ],
         "difficulty": 2,
         "topics": [
-          "algorithms", "conditionals", "filtering"
+          "algorithms",
+          "conditionals",
+          "filtering"
         ]
       },
       {
         "slug": "sublist",
         "name": "Sublist",
         "uuid": "85dc1645-8424-45f2-8653-bc9730887b40",
-        "practices": ["lists"],
-        "prerequisites": ["lists"],
+        "practices": [
+          "lists"
+        ],
+        "prerequisites": [
+          "lists"
+        ],
         "difficulty": 2,
         "topics": [
           "sets"
@@ -502,7 +654,10 @@
         "name": "Sum of Multiples",
         "uuid": "357de34a-c149-4a2c-b6a8-86ffb5eaa152",
         "practices": [],
-        "prerequisites": ["numbers", "conditionals"],
+        "prerequisites": [
+          "numbers",
+          "conditionals"
+        ],
         "difficulty": 2,
         "topics": [
           "math"
@@ -512,11 +667,18 @@
         "slug": "triangle",
         "name": "Triangle",
         "uuid": "a148a999-76ef-40d4-8bf7-96d6ae38876e",
-        "practices": ["floating-point-numbers", "conditionals"],
-        "prerequisites": ["floating-point-numbers", "conditionals"],
+        "practices": [
+          "floating-point-numbers",
+          "conditionals"
+        ],
+        "prerequisites": [
+          "floating-point-numbers",
+          "conditionals"
+        ],
         "difficulty": 2,
         "topics": [
-          "integers", "conditionals"
+          "integers",
+          "conditionals"
         ]
       },
       {
@@ -524,10 +686,15 @@
         "name": "Word Count",
         "uuid": "ea3cf3f6-35de-4da1-a74f-b7a13d89c39a",
         "practices": [],
-        "prerequisites": ["numbers", "conditionals", "strings"],
+        "prerequisites": [
+          "numbers",
+          "conditionals",
+          "strings"
+        ],
         "difficulty": 2,
         "topics": [
-          "regular_expressions", "strings"
+          "regular_expressions",
+          "strings"
         ]
       },
       {
@@ -535,10 +702,15 @@
         "name": "Atbash Cipher",
         "uuid": "7d37e817-d2b7-4554-8885-b02d57d1d788",
         "practices": [],
-        "prerequisites": ["numbers", "conditionals", "strings"],
+        "prerequisites": [
+          "numbers",
+          "conditionals",
+          "strings"
+        ],
         "difficulty": 3,
         "topics": [
-          "algorithms", "regular_expressions"
+          "algorithms",
+          "regular_expressions"
         ]
       },
       {
@@ -546,10 +718,15 @@
         "name": "Beer Song",
         "uuid": "56a4b06b-c48f-4a0f-883a-a08886472b56",
         "practices": [],
-        "prerequisites": ["numbers", "conditionals", "strings"],
+        "prerequisites": [
+          "numbers",
+          "conditionals",
+          "strings"
+        ],
         "difficulty": 3,
         "topics": [
-          "strings", "conditionals"
+          "strings",
+          "conditionals"
         ]
       },
       {
@@ -557,7 +734,9 @@
         "name": "Binary",
         "uuid": "daf3daed-61ef-4964-8d33-e7a739c7a470",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 3,
         "topics": [
           "math"
@@ -568,10 +747,14 @@
         "name": "Binary Search",
         "uuid": "f32c6a4b-0f23-4cd2-95b2-f6e70e4b40b1",
         "practices": [],
-        "prerequisites": ["vectors"],
+        "prerequisites": [
+          "vectors"
+        ],
         "difficulty": 3,
         "topics": [
-          "algorithms", "recursion", "conditionals"
+          "algorithms",
+          "recursion",
+          "conditionals"
         ]
       },
       {
@@ -579,10 +762,13 @@
         "name": "Binary Search Tree",
         "uuid": "53678d3d-de3f-483d-9f02-d2313290786d",
         "practices": [],
-        "prerequisites": ["vectors"],
+        "prerequisites": [
+          "vectors"
+        ],
         "difficulty": 3,
         "topics": [
-          "algorithms", "recursion"
+          "algorithms",
+          "recursion"
         ]
       },
       {
@@ -590,7 +776,10 @@
         "name": "Change",
         "uuid": "7945eb6a-20eb-489d-90bd-020df3d9e2cb",
         "practices": [],
-        "prerequisites": ["numbers", "vectors"],
+        "prerequisites": [
+          "numbers",
+          "vectors"
+        ],
         "difficulty": 3,
         "topics": [
           "algorithms"
@@ -601,10 +790,13 @@
         "name": "Flatten Array",
         "uuid": "6b81cf43-e071-4daf-89a9-d10e213a5751",
         "practices": [],
-        "prerequisites": ["vectors"],
+        "prerequisites": [
+          "vectors"
+        ],
         "difficulty": 3,
         "topics": [
-          "core_functions", "seqs"
+          "core_functions",
+          "seqs"
         ]
       },
       {
@@ -612,7 +804,9 @@
         "name": "Gigasecond",
         "uuid": "798445d8-acad-4b01-b471-3809f46a8a84",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 3,
         "topics": [
           "time"
@@ -623,10 +817,14 @@
         "name": "Grade School",
         "uuid": "a48ff289-baf0-4b13-a5aa-78f6ee91571b",
         "practices": [],
-        "prerequisites": ["numbers", "strings"],
+        "prerequisites": [
+          "numbers",
+          "strings"
+        ],
         "difficulty": 3,
         "topics": [
-          "maps", "core_functions"
+          "maps",
+          "core_functions"
         ]
       },
       {
@@ -634,10 +832,13 @@
         "name": "Grains",
         "uuid": "2e57c7d4-d887-4dba-8483-60e026bdf5ea",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 3,
         "topics": [
-          "integers", "recursion"
+          "integers",
+          "recursion"
         ]
       },
       {
@@ -645,7 +846,9 @@
         "name": "Hexadecimal",
         "uuid": "a4b1b39a-d942-4921-9b67-2bd913ff52a0",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 3,
         "topics": [
           "math"
@@ -656,21 +859,31 @@
         "name": "ISBN Verifier",
         "uuid": "0479e73c-acb3-4a36-949c-a2065bfb57e5",
         "practices": [],
-        "prerequisites": ["strings", "booleans"],
+        "prerequisites": [
+          "strings",
+          "booleans"
+        ],
         "difficulty": 3,
         "topics": [
-          "regular_expressions", "conditionals"
+          "regular_expressions",
+          "conditionals"
         ]
       },
       {
         "slug": "isogram",
         "name": "Isogram",
         "uuid": "ced9928a-05dc-4afa-9645-a2f2e548fe6e",
-        "practices": ["strings"],
-        "prerequisites": ["strings", "booleans"],
+        "practices": [
+          "strings"
+        ],
+        "prerequisites": [
+          "strings",
+          "booleans"
+        ],
         "difficulty": 3,
         "topics": [
-          "filtering", "strings"
+          "filtering",
+          "strings"
         ]
       },
       {
@@ -678,10 +891,13 @@
         "name": "Kindergarten Garden",
         "uuid": "e40c52a7-9509-4a4d-9ad6-8ca7a3bdca1d",
         "practices": [],
-        "prerequisites": ["strings"],
+        "prerequisites": [
+          "strings"
+        ],
         "difficulty": 3,
         "topics": [
-          "strings", "conditionals"
+          "strings",
+          "conditionals"
         ]
       },
       {
@@ -689,10 +905,14 @@
         "name": "Leap",
         "uuid": "336aa5ec-f868-4a8a-9fd2-989b6c2fd0be",
         "practices": [],
-        "prerequisites": ["conditionals", "numbers"],
+        "prerequisites": [
+          "conditionals",
+          "numbers"
+        ],
         "difficulty": 3,
         "topics": [
-          "integers", "conditionals"
+          "integers",
+          "conditionals"
         ]
       },
       {
@@ -700,7 +920,9 @@
         "name": "Pascal's Triangle",
         "uuid": "29b16274-b413-4231-a1c7-aff8f93c9b7e",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 3,
         "topics": [
           "math"
@@ -711,7 +933,9 @@
         "name": "Perfect Numbers",
         "uuid": "e10b7063-75ac-46d6-848c-c50db87bddc7",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 3,
         "topics": [
           "math"
@@ -722,7 +946,9 @@
         "name": "Phone Number",
         "uuid": "41b95380-2bba-407b-b400-7d8a627f18fa",
         "practices": [],
-        "prerequisites": ["strings"],
+        "prerequisites": [
+          "strings"
+        ],
         "difficulty": 3,
         "topics": [
           "parsing"
@@ -733,7 +959,9 @@
         "name": "Prime Factors",
         "uuid": "96005f0f-5153-4915-9f28-326b41b63fab",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 3,
         "topics": [
           "math"
@@ -744,21 +972,36 @@
         "name": "Proverb",
         "uuid": "540d93a9-707f-4043-b46c-1dc83f2d50d8",
         "practices": [],
-        "prerequisites": ["strings", "vectors"],
+        "prerequisites": [
+          "strings",
+          "vectors"
+        ],
         "difficulty": 3,
         "topics": [
-          "vectors", "strings", "conditionals"
+          "vectors",
+          "strings",
+          "conditionals"
         ]
       },
       {
         "slug": "say",
         "name": "Say",
         "uuid": "0ff7efbe-855e-4a81-9b64-f07d1557813f",
-        "practices": ["numbers", "strings", "conditionals"],
-        "prerequisites": ["numbers", "strings", "conditionals"],
+        "practices": [
+          "numbers",
+          "strings",
+          "conditionals"
+        ],
+        "prerequisites": [
+          "numbers",
+          "strings",
+          "conditionals"
+        ],
         "difficulty": 3,
         "topics": [
-          "strings", "integers", "exception_handling"
+          "strings",
+          "integers",
+          "exception_handling"
         ]
       },
       {
@@ -766,7 +1009,10 @@
         "name": "Trinary",
         "uuid": "f8a7f5eb-e317-4ffa-bdef-6e30a36511eb",
         "practices": [],
-        "prerequisites": ["numbers", "strings"],
+        "prerequisites": [
+          "numbers",
+          "strings"
+        ],
         "difficulty": 3,
         "topics": [
           "math"
@@ -777,7 +1023,10 @@
         "name": "Allergies",
         "uuid": "a71bc471-8359-4019-8ad1-e456774388b7",
         "practices": [],
-        "prerequisites": ["vectors", "booleans"],
+        "prerequisites": [
+          "vectors",
+          "booleans"
+        ],
         "difficulty": 4,
         "topics": [
           "conditionals"
@@ -788,18 +1037,26 @@
         "name": "Crypto Square",
         "uuid": "7387b271-d765-497c-9b85-481c947439b6",
         "practices": [],
-        "prerequisites": ["strings"],
+        "prerequisites": [
+          "strings"
+        ],
         "difficulty": 4,
         "topics": [
-          "algorithms", "sorting", "regular_expressions"
+          "algorithms",
+          "sorting",
+          "regular_expressions"
         ]
       },
       {
         "slug": "difference-of-squares",
         "name": "Difference of Squares",
         "uuid": "6f69d8fe-9da2-48f9-a635-b9f54b626daf",
-        "practices": ["numbers"],
-        "prerequisites": ["numbers"],
+        "practices": [
+          "numbers"
+        ],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 4,
         "topics": [
           "math"
@@ -810,18 +1067,27 @@
         "name": "Dominoes",
         "uuid": "da04dfa4-5a66-49a5-a7ca-6320cfa4158f",
         "practices": [],
-        "prerequisites": ["numbers", "booleans"],
+        "prerequisites": [
+          "numbers",
+          "booleans"
+        ],
         "difficulty": 4,
         "topics": [
-          "graph_theory", "games"
+          "graph_theory",
+          "games"
         ]
       },
       {
         "slug": "largest-series-product",
         "name": "Largest Series Product",
         "uuid": "2978747b-86e6-48f0-aef2-2d00714c8c5e",
-        "practices": ["numbers"],
-        "prerequisites": ["numbers", "strings"],
+        "practices": [
+          "numbers"
+        ],
+        "prerequisites": [
+          "numbers",
+          "strings"
+        ],
         "difficulty": 4,
         "topics": [
           "math"
@@ -832,18 +1098,25 @@
         "name": "Meetup",
         "uuid": "81e37fc7-13cd-46a9-987c-c9bda4ff1ead",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 4,
         "topics": [
-          "dates", "time"
+          "dates",
+          "time"
         ]
       },
       {
         "slug": "octal",
         "name": "Octal",
         "uuid": "fa596153-0cbe-40e9-a4d6-1a66fe6ef7e5",
-        "practices": ["numbers"],
-        "prerequisites": ["numbers"],
+        "practices": [
+          "numbers"
+        ],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 4,
         "topics": [
           "math"
@@ -854,7 +1127,11 @@
         "name": "Spiral Matrix",
         "uuid": "71d92de0-9f54-4e07-9a8f-fcd477f00219",
         "practices": [],
-        "prerequisites": ["vectors", "numbers", "conditionals"],
+        "prerequisites": [
+          "vectors",
+          "numbers",
+          "conditionals"
+        ],
         "difficulty": 4,
         "topics": [
           "recursivity"
@@ -865,10 +1142,13 @@
         "name": "Clock",
         "uuid": "83b7ffc2-aadd-4c26-9794-5e7f851eea8b",
         "practices": [],
-        "prerequisites": ["numbers"],
+        "prerequisites": [
+          "numbers"
+        ],
         "difficulty": 5,
         "topics": [
-          "dates", "time"
+          "dates",
+          "time"
         ]
       },
       {
@@ -876,7 +1156,9 @@
         "name": "Diamond",
         "uuid": "cc0d15ad-d562-458c-8d05-4b213693cfb9",
         "practices": [],
-        "prerequisites": ["strings"],
+        "prerequisites": [
+          "strings"
+        ],
         "difficulty": 5,
         "topics": [
           "transforming"
@@ -887,10 +1169,14 @@
         "name": "Luhn",
         "uuid": "e2d0287c-2fe9-4f46-abfb-4db855d7489b",
         "practices": [],
-        "prerequisites": ["numbers", "strings"],
+        "prerequisites": [
+          "numbers",
+          "strings"
+        ],
         "difficulty": 5,
         "topics": [
-          "integers", "conditionals"
+          "integers",
+          "conditionals"
         ]
       },
       {
@@ -898,7 +1184,10 @@
         "name": "Sieve",
         "uuid": "0e87bb30-3e7e-49dc-9f80-2d291dfb0111",
         "practices": [],
-        "prerequisites": ["vectors", "numbers"],
+        "prerequisites": [
+          "vectors",
+          "numbers"
+        ],
         "difficulty": 5,
         "topics": [
           "math"
@@ -908,22 +1197,36 @@
         "slug": "robot-simulator",
         "name": "Robot Simulator",
         "uuid": "e69ecf2e-8629-4d20-a90f-6de7536247ce",
-        "practices": ["conditionals"],
-        "prerequisites": ["conditionals"],
+        "practices": [
+          "conditionals"
+        ],
+        "prerequisites": [
+          "conditionals"
+        ],
         "difficulty": 6,
         "topics": [
-          "strings", "conditionals"
+          "strings",
+          "conditionals"
         ]
       },
       {
         "slug": "wordy",
         "name": "Wordy",
         "uuid": "5528f567-2b5b-498a-b166-984b2c90f6a5",
-        "practices": ["numbers", "strings", "conditionals"],
-        "prerequisites": ["numbers", "strings", "conditionals"],
+        "practices": [
+          "numbers",
+          "strings",
+          "conditionals"
+        ],
+        "prerequisites": [
+          "numbers",
+          "strings",
+          "conditionals"
+        ],
         "difficulty": 6,
         "topics": [
-          "regular_expressions", "parsing"
+          "regular_expressions",
+          "parsing"
         ]
       },
       {
@@ -931,7 +1234,10 @@
         "name": "Bank Account",
         "uuid": "d67a7a46-050c-4472-84bd-2e216079a452",
         "practices": [],
-        "prerequisites": ["conditionals", "numbers"],
+        "prerequisites": [
+          "conditionals",
+          "numbers"
+        ],
         "difficulty": 7,
         "topics": [
           "conditionals"
@@ -942,7 +1248,10 @@
         "name": "Matching Brackets",
         "uuid": "86f005c6-1754-4907-becb-a0997fd52d05",
         "practices": [],
-        "prerequisites": ["strings", "booleans"],
+        "prerequisites": [
+          "strings",
+          "booleans"
+        ],
         "difficulty": 7,
         "topics": [
           "parsing"
@@ -953,10 +1262,15 @@
         "name": "Minesweeper",
         "uuid": "e07e545c-997f-424b-b4ca-34fd7cc06bf7",
         "practices": [],
-        "prerequisites": ["vectors", "conditionals", "strings"],
+        "prerequisites": [
+          "vectors",
+          "conditionals",
+          "strings"
+        ],
         "difficulty": 7,
         "topics": [
-          "algorithms", "games"
+          "algorithms",
+          "games"
         ]
       },
       {
@@ -964,7 +1278,10 @@
         "name": "Poker",
         "uuid": "f842d531-e2a1-4573-90c3-b77177df4787",
         "practices": [],
-        "prerequisites": ["conditionals", "strings"],
+        "prerequisites": [
+          "conditionals",
+          "strings"
+        ],
         "difficulty": 7,
         "topics": [
           "games"
@@ -975,10 +1292,15 @@
         "name": "Queen Attack",
         "uuid": "edd7932d-50a6-4350-b945-2bb8a8c891c7",
         "practices": [],
-        "prerequisites": ["booleans", "conditionals", "numbers"],
+        "prerequisites": [
+          "booleans",
+          "conditionals",
+          "numbers"
+        ],
         "difficulty": 7,
         "topics": [
-          "equality", "conditionals"
+          "equality",
+          "conditionals"
         ]
       },
       {
@@ -986,7 +1308,9 @@
         "name": "Go Counting",
         "uuid": "f0210181-7fe5-49f9-9036-be4256612b3e",
         "practices": [],
-        "prerequisites": ["strings"],
+        "prerequisites": [
+          "strings"
+        ],
         "difficulty": 9,
         "topics": [
           "games"
@@ -997,7 +1321,10 @@
         "name": "POV",
         "uuid": "ca557f64-9e2d-4ab0-8788-4133764ef703",
         "practices": [],
-        "prerequisites": ["strings", "vectors"],
+        "prerequisites": [
+          "strings",
+          "vectors"
+        ],
         "difficulty": 10,
         "topics": [
           "conditionals"
@@ -1008,7 +1335,10 @@
         "name": "Yacht",
         "uuid": "459c8468-efc4-46a6-bbc0-9937167827a7",
         "practices": [],
-        "prerequisites": ["strings", "vectors"],
+        "prerequisites": [
+          "strings",
+          "vectors"
+        ],
         "difficulty": 5,
         "topics": [
           "conditionals"
@@ -1019,11 +1349,26 @@
         "name": "Zipper",
         "uuid": "331b2960-baae-4331-a462-a434fd6e8767",
         "practices": [],
-        "prerequisites": ["strings", "vectors"],
+        "prerequisites": [
+          "strings",
+          "vectors"
+        ],
         "difficulty": 9,
         "topics": [
           "conditionals"
         ]
+      },
+      {
+        "slug": "list-ops",
+        "name": "List Ops",
+        "uuid": "699fb4ec-210d-454d-a41a-f7014abf8423",
+        "practices": [],
+        "prerequisites": [
+          "lists",
+          "closures"
+        ],
+        "difficulty": 5,
+        "status": "wip"
       }
     ]
   },
@@ -1127,14 +1472,14 @@
     }
   ],
   "tags": [
+    "execution_mode/compiled",
     "paradigm/declarative",
     "paradigm/functional",
-    "typing/dynamic",
-    "execution_mode/compiled",
-    "platform/windows",
-    "platform/mac",
     "platform/linux",
+    "platform/mac",
+    "platform/windows",
     "runtime/jvm",
+    "typing/dynamic",
     "used_for/artificial_intelligence",
     "used_for/backends",
     "used_for/cross_platform_development",
diff --git a/exercises/concept/annalyns-infiltration/.docs/hints.md b/exercises/concept/annalyns-infiltration/.docs/hints.md
index 6d7ccd6..9d417d4 100644
--- a/exercises/concept/annalyns-infiltration/.docs/hints.md
+++ b/exercises/concept/annalyns-infiltration/.docs/hints.md
@@ -6,11 +6,11 @@ We need to check if the knight is awake and _invert_ its truth value. This can b
 
 ## 2. Check if the group can be spied upon
 
-We want the function to return `true` if _any_ of the supplied predicates are true. This can be done using the [`or`][or] function.
+We want the function to return `true` if _any_ of the supplied predicates are `true`. This can be done using the [`or`][or] function.
 
 ## 3. Check if the prisoner can be signalled
 
-We want to return `true` if and only if _all_ of the supplied predicates are truthy. This can be done with the [`and`][and] function.
+We want to return `true` if and only if _all_ of the supplied predicates are `true`. This can be done with the [`and`][and] function.
 
 ## 4. Check if the prisoner can be freed
 
diff --git a/exercises/concept/annalyns-infiltration/.docs/instructions.md b/exercises/concept/annalyns-infiltration/.docs/instructions.md
index 2b2b330..b5b7976 100644
--- a/exercises/concept/annalyns-infiltration/.docs/instructions.md
+++ b/exercises/concept/annalyns-infiltration/.docs/instructions.md
@@ -20,7 +20,7 @@ You have four tasks: to implement the logic for determining if the above actions
 
 ## 1. Check if a fast attack can be made
 
-Implement the `can-fast-attack?` function that takes a boolean value that indicates if the knight is awake. This function returns `true` if a fast attack can be made based on the state of the knight. Otherwise, returns `false`:
+Implement the `can-fast-attack?` function, which takes a boolean value indicating whether the knight is awake. The function returns `true` if a fast attack can be made based on the state of the knight. Otherwise, it returns `false`:
 
 ```clojure
 (def knight-awake? true)
@@ -31,7 +31,7 @@ Implement the `can-fast-attack?` function that takes a boolean value that indica
 
 ## 2. Check if the group can be spied upon
 
-Implement the `can-spy?` function that takes three boolean values, indicating if the knight, archer and the prisoner, respectively, are awake. The function returns `true` if the group can be spied upon, based on the state of the three characters. Otherwise, returns `false`:
+Implement the `can-spy?` function, which takes three boolean values indicating whether the knight, archer and the prisoner, respectively, are awake. The function returns `true` if the group can be spied upon based on the state of the three characters. Otherwise, it returns `false`:
 
 ```clojure
 (def knight-awake? false)
@@ -44,7 +44,7 @@ Implement the `can-spy?` function that takes three boolean values, indicating if
 
 ## 3. Check if the prisoner can be signalled
 
-Implement the `can-signal-prisoner?` function that takes two boolean values, indicating if the archer and the prisoner, respectively, are awake. The function returns `true` if the prisoner can be signalled, based on the state of the two characters. Otherwise, returns `false`:
+Implement the `can-signal-prisoner?` function, which takes two boolean values indicating whether the archer and the prisoner, respectively, are awake. The function returns `true` if the prisoner can be signalled based on the state of the two characters. Otherwise, it returns `false`:
 
 ```clojure
 (def archer-awake? false)
@@ -56,7 +56,7 @@ Implement the `can-signal-prisoner?` function that takes two boolean values, ind
 
 ## 4. Check if the prisoner can be freed
 
-Implement the `can-free-prisoner?` function that takes four boolean values. The first three parameters indicate if the knight, archer and the prisoner, respectively, are awake. The last parameter indicates if Annalyn's pet dog is present. The function returns `true` if the prisoner can be freed based on the state of the three characters and Annalyn's pet dog presence. Otherwise, it returns `false`:
+Implement the `can-free-prisoner?` function, which takes four boolean values. The first three parameters indicate whether the knight, archer and the prisoner, respectively, are awake. The last parameter indicates whether Annalyn's pet dog is present. The function returns `true` if the prisoner can be freed based on the state of the three characters and Annalyn's pet dog presence. Otherwise, it returns `false`:
 
 ```clojure
 (def knight-awake? false)
diff --git a/exercises/concept/annalyns-infiltration/.docs/introduction.md b/exercises/concept/annalyns-infiltration/.docs/introduction.md
index 6fbaa4a..e116426 100644
--- a/exercises/concept/annalyns-infiltration/.docs/introduction.md
+++ b/exercises/concept/annalyns-infiltration/.docs/introduction.md
@@ -2,6 +2,6 @@
 
 Booleans in Clojure are represented by `true` or `false`.
 
-Predicate functions (functions which return a boolean) will typically end with a quesion mark (`?`), but this is by convention only.
+Predicate functions (functions which return a boolean) will typically end with a question mark (`?`), but this is by convention only.
 
 The core library includes functions for logical operators such as `not`, `and`, and `or`.
diff --git a/exercises/concept/bird-watcher/.docs/hints.md b/exercises/concept/bird-watcher/.docs/hints.md
index e32ffb0..7dd02b2 100644
--- a/exercises/concept/bird-watcher/.docs/hints.md
+++ b/exercises/concept/bird-watcher/.docs/hints.md
@@ -6,7 +6,7 @@
 
 ## 2. Check how many birds visited today
 
-- The [`last`][last] function can be used to return the last item in a sequential collection.
+- The [`peek`][peek] function can be used to return the last item in a vector.
 
 ## 3. Increment today's count
 
@@ -20,13 +20,13 @@
 
 - The [`every?`][every?] function can be used to check if all items in a collection satisfy a given predicate.
 
-- The [`pos?`][pos?] predicate returns true if a number is greater than zero.
+- The [`pos?`][pos?] predicate returns `true` if a number is greater than zero.
 
-- The [`not`][not] function returns true if an expression evaluates to logical false.
+- The [`not`][not] function returns `true` if an expression evaluates to logical false.
 
 ## 5. Calculate the number of visiting birds for the first number of days
 
-- The [`take`][take] function can be used return a sequence of the first n items in a collection.
+- The [`take`][take] function can be used to return a sequence of the first n items in a collection.
 
 - The [`reduce`][reduce] function can be used to add a collection of values together.
 
@@ -36,7 +36,7 @@
 
 ## 7.  Check for odd week
 
-- The [`=`][equality] function can be used to test equality.
+- The [`=`][equality] function can be used to test for equality.
 
 [count]: https://clojuredocs.org/clojure.core/count
 [dec]: https://clojuredocs.org/clojure.core/dec
@@ -44,7 +44,7 @@
 [every?]: https://clojuredocs.org/clojure.core/every_q
 [filter]: https://clojuredocs.org/clojure.core/filter
 [inc]: https://clojuredocs.org/clojure.core/inc
-[last]: https://clojuredocs.org/clojure.core/last
+[peek]: https://clojuredocs.org/clojure.core/peek
 [not]: https://clojuredocs.org/clojure.core/not
 [pos?]: https://clojuredocs.org/clojure.core/pos_q
 [reduce]: https://clojuredocs.org/clojure.core/reduce
diff --git a/exercises/concept/bird-watcher/.docs/instructions.md b/exercises/concept/bird-watcher/.docs/instructions.md
index a3ce8b3..121782c 100755
--- a/exercises/concept/bird-watcher/.docs/instructions.md
+++ b/exercises/concept/bird-watcher/.docs/instructions.md
@@ -62,7 +62,7 @@ Implement the `busy-days` function to return the number of busy days:
 
 ## 7. Check for odd week
 
-Over the last year, you've found that some weeks for the same, odd pattern, where the counts alternate between one and zero birds visiting. Implement the `odd-week?` function that returns `true` if the bird count pattern of this week matches the odd pattern:
+Over the last year, you've found that some weeks form the same, odd pattern, where the counts alternate between one and zero birds visiting. Implement the `odd-week?` function that returns `true` if the bird count pattern of this week matches the odd pattern:
 
 ```clojure
 (odd-week? [1 0 1 0 1 0 1])
diff --git a/exercises/concept/bird-watcher/.meta/exemplar.clj b/exercises/concept/bird-watcher/.meta/exemplar.clj
index 7473988..cfdb65a 100644
--- a/exercises/concept/bird-watcher/.meta/exemplar.clj
+++ b/exercises/concept/bird-watcher/.meta/exemplar.clj
@@ -3,13 +3,13 @@
 (def last-week [0 2 5 3 7 8 4])
 
 (defn today [birds]
-  (last birds))
+  (peek birds))
 
 (defn inc-bird [birds]
   (update birds (dec (count birds)) inc))
 
 (defn day-without-birds? [birds]
-  (pos? (count (filter zero? birds))))
+  (not (every? pos? birds)))
 
 (defn n-days-count [birds n]
   (reduce + (take n birds)))
diff --git a/exercises/concept/card-games/.docs/hints.md b/exercises/concept/card-games/.docs/hints.md
index 3013115..d37b8cc 100644
--- a/exercises/concept/card-games/.docs/hints.md
+++ b/exercises/concept/card-games/.docs/hints.md
@@ -11,18 +11,18 @@
 ## 3. Finding Prior Rounds
 
 - The [`some`][some] function will return the first logically true value in a collection.
-- [`boolean`][boolean] is useful in cases where a function returns a value but the tests are expecting a boolean.
+- The [`boolean`][boolean] function is useful in cases where a function returns a value but the tests are expecting a boolean.
 
 ## 4. Averaging Card Values
 
 - Recall basic arithmetic operators such as [`/`][division] and [`+`][addition].
 - A collection of numbers can be summed using either [`apply`][apply] or [`reduce`][reduce].
 - The [`count`][count] function returns the number of items in a collection.
-- The `/` function returns a ratio type when operating on integers. You can use [`double`][double] to convert a ratio to a decimal.
+- The [`/`][division] function returns a ratio type when operating on integers. You can use [`double`][double] to convert a ratio to a decimal.
 
 ## 5. Alternate Averages
 
-- There are sequence functions to return the [`first`][first] or [`last`] element of a sequential collection.
+- There are sequence functions to return the [`first`][first] or [`last`][last] element of a sequential collection.
 - To find the middle card you can divide the number of cards by 2, round to the lowest integer with [`int`][int], and retrieve the card at that index using [`nth`][nth].
 - Use [`or`][or] to determine if either condition is true.
 
diff --git a/exercises/concept/log-levels/.docs/hints.md b/exercises/concept/log-levels/.docs/hints.md
index feabaa9..c31a45b 100644
--- a/exercises/concept/log-levels/.docs/hints.md
+++ b/exercises/concept/log-levels/.docs/hints.md
@@ -11,13 +11,13 @@
 
 ## 2. Get log level from a log line
 
-- A `string` can be converted to lowercase using [`lowercase`][lowercase].
+- A `string` can be converted to lowercase using [`lower-case`][lower-case].
 
 ## 3. Reformat a log line
 
 - The [`str`][str] function can be used to concatenate strings.
 
-[lowercase]: https://clojuredocs.org/clojure.string/lower-case
+[lower-case]: https://clojuredocs.org/clojure.string/lower-case
 [split]: https://clojuredocs.org/clojure.string/split
 [str]: https://clojuredocs.org/clojure.core/str
 [string-ns]: https://clojuredocs.org/clojure.string
diff --git a/exercises/concept/squeaky-clean/.docs/hints.md b/exercises/concept/squeaky-clean/.docs/hints.md
index efca0f8..30b79b0 100644
--- a/exercises/concept/squeaky-clean/.docs/hints.md
+++ b/exercises/concept/squeaky-clean/.docs/hints.md
@@ -6,20 +6,20 @@ The built-in `clojure.string` library contains a [`replace`][str-replace] functi
 
 ## 2. Replace control characters with the upper case string "CTRL"
 
-Here we need to use the [`isISOControl`][java-ctrl] method from Java's `Character` class.
+You can use the [`isISOControl`][java-ctrl] method from Java's `Character` class.
 
 ## 3. Convert kebab-case to camelCase
 
-Try separating the string on the hyphens with [`string/split`][str-split].
-If there are no hyphens, we need to pass the original string. You can check using [string/includes?][str-includes]. Use [string/upper-case][str-upper-case] to make the "humps" of the camel. Finally, concatenate them with [string/join][str-join].
+Try separating the string on the hyphens with [`split`][str-split].
+If it contains no hyphens, you should return it as it is. You can check this using [`includes?`][str-includes]. Use [`upper-case`][str-upper-case] to make the "humps" of the camel. Finally, concatenate them with [`join`][str-join].
 
 ## 4. Omit characters that are not letters
 
-You can filter characters using [Character/isLetter][java-isletter] as a predicate.
+You can filter characters using [`Character/isLetter`][java-isletter] as a predicate.
 
-## 5. Omit Greek lower case letters
+## 5. Omit Greek lowercase letters
 
-The lower-case Greek letters are Unicode points `\u03B1` to `\u03C9`, or integer values 945 through 969.
+The Greek lowercase letters are Unicode points `\u03B1` to `\u03C9`, or integer values 945 through 969.
 
 [str-join]: https://clojuredocs.org/clojure.string/join
 [str-upper-case]: https://clojuredocs.org/clojure.string/upper-case
diff --git a/exercises/concept/squeaky-clean/.docs/instructions.md b/exercises/concept/squeaky-clean/.docs/instructions.md
index e697513..83a1d7c 100644
--- a/exercises/concept/squeaky-clean/.docs/instructions.md
+++ b/exercises/concept/squeaky-clean/.docs/instructions.md
@@ -3,14 +3,14 @@
 In this exercise you will implement a partial set of utility routines 
 to help a developer clean up identifier names.
 
-In the 5 tasks you will gradually build up the routine `clean`. 
+In the 5 tasks you will gradually build up the function `clean`.
 A valid identifier comprises zero or more letters and underscores.
 
 In all cases the input string is guaranteed to be non-nil. 
-If an empty string is passed to the `clean` function, 
+If an empty string is passed to the function,
 an empty string should be returned.
 
-Note that the caller should avoid calling the routine `clean` 
+Note that the caller should avoid calling the function
 with an empty identifier since such identifiers are ineffectual.
 
 ## 1. Replace any spaces encountered with underscores
@@ -55,7 +55,7 @@ Note: The underscores must be preserved from the previous step.
 ;; => ""
 ```
 
-## 5. Omit Greek lower case letters
+## 5. Omit Greek lowercase letters
 
 Modify the `clean` function to omit any Greek letters in the range 'α' to 'ω'.
 
diff --git a/exercises/practice/complex-numbers/.docs/instructions.append.md b/exercises/practice/complex-numbers/.docs/instructions.append.md
new file mode 100644
index 0000000..1c6bfad
--- /dev/null
+++ b/exercises/practice/complex-numbers/.docs/instructions.append.md
@@ -0,0 +1,4 @@
+# Instructions append
+
+**Note:** The instructions ask for the exponent function because they are synced with a shared repository to maintain consistency across all language tracks.
+However, for this exercise in the Clojure track, you don't need to implement it.
diff --git a/exercises/practice/leap/.approaches/boolean-operations/content.md b/exercises/practice/leap/.approaches/boolean-operations/content.md
index 01a049d..0bd137b 100644
--- a/exercises/practice/leap/.approaches/boolean-operations/content.md
+++ b/exercises/practice/leap/.approaches/boolean-operations/content.md
@@ -11,7 +11,7 @@
 ```
 
 At the core of this approach, three checks are returning three boolean values.
-We can use [Boolean logic](https://en.wikipedia.org/wiki/Boolean_algebra) to combine the results. Multiple variations are possible. One is shown above, but the below is also possible.
+We can use [Boolean logic](https://en.wikipedia.org/wiki/Boolean_algebra) to combine the results. Multiple variations are possible. One is shown above, but the one below is also possible.
 
 ```clojure
 (defn leap-year? [year]
@@ -22,7 +22,7 @@ We can use [Boolean logic](https://en.wikipedia.org/wiki/Boolean_algebra) to com
 
 ## Being explicit
 
-The above examples use a private function `divides?` to be more explicit, to show what logical check is done rather than what operation is performed.
+The above examples use a private function `divides?` to be more explicit, showing what logical check is done rather than what operation is performed.
 This is common in Clojure code, but it is also possible to do the checks directly.
 
 ```clojure
@@ -34,7 +34,7 @@ This is common in Clojure code, but it is also possible to do the checks directl
 
 In this example, in addition, instead of negating the second check, we check if the reminder from the division is greater than zero.
 
-If the concern is that defining `divides?` adds confusion, because it is not known in the only place where it is used, `letfn` can be used. Here is another example.
+If the concern is that defining `divides?` adds confusion because it is not known in the only place where it is used, `letfn` can be used instead. Here is another example.
 
 ```clojure
 (defn leap-year? [year]
diff --git a/exercises/practice/leap/.approaches/flow-control/content.md b/exercises/practice/leap/.approaches/flow-control/content.md
index e65b7a7..1bf0618 100644
--- a/exercises/practice/leap/.approaches/flow-control/content.md
+++ b/exercises/practice/leap/.approaches/flow-control/content.md
@@ -39,7 +39,7 @@ Another option is `cond` which allows for evaluating multiple conditions, simila
     :else false))
 ```
 
-A very similar alternative is to use the `condp` macro, which takes a predicate and applies it to a series of test value and expected result pairs.
+A very similar alternative is to use the `condp` macro, which takes a predicate and applies it to a series of test values and expected result pairs.
 
 ```clojure
 (defn leap-year? [year]
@@ -50,7 +50,7 @@ A very similar alternative is to use the `condp` macro, which takes a predicate
         false))
 ```
 
-When using both `cond` and `condp,` the other matters as the first true condition stops evaluating the list and determines the result.
+When using both `cond` and `condp,` the order matters, as the first true condition stops evaluating the list and determines the result.
 
 ## Case
 
diff --git a/exercises/practice/leap/.approaches/introduction.md b/exercises/practice/leap/.approaches/introduction.md
index 990fcf4..07b559b 100644
--- a/exercises/practice/leap/.approaches/introduction.md
+++ b/exercises/practice/leap/.approaches/introduction.md
@@ -44,7 +44,7 @@ Explore the details in the [boolean operations approach][boolean-approach].
 
 ## Approach: conditional branching
 
-Instead of combining the logical expressions, we can use conditional branching with functions like `if` or `cond` to perform the necessary checks and determin if a given years is a leap one. 
+Instead of combining the logical expressions, we can use conditional branching with functions like `if` or `cond` to perform the necessary checks and determine if a given year is a leap year.
 
 ```clojure
 (if (divides? year 100) 
@@ -56,7 +56,7 @@ In the [flow control expressions approach][flow-control-approach], we discuss th
 
 ## Approach: data shapes
 
-This is a little less straight forward approach, but with a little bit of setup we can get to a `leap-year?` function that looks like so:
+This is a slightly less straightforward approach, but with a bit of setup, we can get to a `leap-year?` function that looks like so:
 
 ```clojure
 (defn leap-year? [year]
diff --git a/exercises/practice/list-ops/.docs/instructions.append.md b/exercises/practice/list-ops/.docs/instructions.append.md
new file mode 100644
index 0000000..e226d47
--- /dev/null
+++ b/exercises/practice/list-ops/.docs/instructions.append.md
@@ -0,0 +1,8 @@
+# Instructions append
+
+## Implementation tips
+
+- The instructions use lists because they are synced with a shared repository to maintain consistency across tracks.
+  However, for this exercise in the Clojure track, you should assume that you are working with vectors instead of lists.
+- It is important not to reuse existing Clojure built-in functions with similar functionality, as doing so would diminish the intended learning value of the exercise.
+  Some of these functions include `into`, `concat`, `cat`, `lazy-cat`, `mapcat`, `flatten`, `filter`, `filterv`, `remove`, `count`, `map`, `mapv`, `reduce`, `transduce`, `reverse`, and `rseq` from the **clojure.core** namespace.
diff --git a/exercises/practice/list-ops/.docs/instructions.md b/exercises/practice/list-ops/.docs/instructions.md
new file mode 100644
index 0000000..ebc5dff
--- /dev/null
+++ b/exercises/practice/list-ops/.docs/instructions.md
@@ -0,0 +1,19 @@
+# Instructions
+
+Implement basic list operations.
+
+In functional languages list operations like `length`, `map`, and `reduce` are very common.
+Implement a series of basic list operations, without using existing functions.
+
+The precise number and names of the operations to be implemented will be track dependent to avoid conflicts with existing names, but the general operations you will implement include:
+
+- `append` (_given two lists, add all items in the second list to the end of the first list_);
+- `concatenate` (_given a series of lists, combine all items in all lists into one flattened list_);
+- `filter` (_given a predicate and a list, return the list of all items for which `predicate(item)` is True_);
+- `length` (_given a list, return the total number of items within it_);
+- `map` (_given a function and a list, return the list of the results of applying `function(item)` on all items_);
+- `foldl` (_given a function, a list, and initial accumulator, fold (reduce) each item into the accumulator from the left_);
+- `foldr` (_given a function, a list, and an initial accumulator, fold (reduce) each item into the accumulator from the right_);
+- `reverse` (_given a list, return a list with all the original items, but in reversed order_).
+
+Note, the ordering in which arguments are passed to the fold functions (`foldl`, `foldr`) is significant.
diff --git a/exercises/practice/list-ops/.meta/config.json b/exercises/practice/list-ops/.meta/config.json
new file mode 100644
index 0000000..1035f4f
--- /dev/null
+++ b/exercises/practice/list-ops/.meta/config.json
@@ -0,0 +1,21 @@
+{
+  "authors": [
+    "bobbicodes"
+  ],
+  "contributors": [
+    "kahgoh",
+    "tasxatzial"
+  ],
+  "files": {
+    "solution": [
+      "src/list_ops.clj"
+    ],
+    "test": [
+      "test/list_ops_test.clj"
+    ],
+    "example": [
+      ".meta/src/example.clj"
+    ]
+  },
+  "blurb": "Implement basic list operations."
+}
diff --git a/exercises/practice/list-ops/.meta/src/example.clj b/exercises/practice/list-ops/.meta/src/example.clj
new file mode 100644
index 0000000..c4bc083
--- /dev/null
+++ b/exercises/practice/list-ops/.meta/src/example.clj
@@ -0,0 +1,34 @@
+(ns list-ops)
+
+(defn foldl [f coll init]
+  (loop [acc init l coll]
+    (cond
+      (empty? l) acc
+      :else (recur (f acc (first l)) (rest l)))))
+
+(defn append [coll1 coll2]
+  (list-ops/foldl (fn [acc elem] (conj acc elem)) coll2 coll1))
+
+(defn concatenate [coll]
+  (cond
+    (empty? coll) []
+    :else (list-ops/foldl (fn [acc elem] (list-ops/append acc elem)) (rest coll) (first coll))))
+
+(defn select-if [pred coll]
+  (loop [acc [] l coll]
+    (cond
+      (empty? l) acc
+      (pred (first l)) (recur (conj acc (first l)) (rest l))
+      :else (recur acc (rest l)))))
+
+(defn length [coll]
+  (list-ops/foldl (fn [acc elem] (+ acc 1)) coll 0))
+
+(defn apply-to-each [f coll]
+  (list-ops/foldl (fn [acc elem] (conj acc (f elem))) coll []))
+
+(defn reverse-order [coll]
+  (list-ops/foldl (fn [acc elem] (cons elem acc)) coll []))
+
+(defn foldr [f coll init]
+  (list-ops/foldl f (list-ops/reverse-order coll) init))
diff --git a/exercises/practice/list-ops/deps.edn b/exercises/practice/list-ops/deps.edn
new file mode 100644
index 0000000..561c3e2
--- /dev/null
+++ b/exercises/practice/list-ops/deps.edn
@@ -0,0 +1,6 @@
+{:aliases {:test {:extra-paths ["test"]
+                 :extra-deps {io.github.cognitect-labs/test-runner 
+                              {:git/url "https://github.com/cognitect-labs/test-runner.git"
+                               :sha "705ad25bbf0228b1c38d0244a36001c2987d7337"}}
+                 :main-opts ["-m" "cognitect.test-runner"]
+                 :exec-fn cognitect.test-runner.api/test}}}
\ No newline at end of file
diff --git a/exercises/practice/list-ops/project.clj b/exercises/practice/list-ops/project.clj
new file mode 100644
index 0000000..31eb1b1
--- /dev/null
+++ b/exercises/practice/list-ops/project.clj
@@ -0,0 +1,4 @@
+(defproject list-ops "0.1.0-SNAPSHOT"
+  :description "list-ops exercise."
+  :url "https://github.com/exercism/clojure/tree/main/exercises/list-ops"
+  :dependencies [[org.clojure/clojure "1.10.0"]])
diff --git a/exercises/practice/list-ops/src/list_ops.clj b/exercises/practice/list-ops/src/list_ops.clj
new file mode 100644
index 0000000..28d30ea
--- /dev/null
+++ b/exercises/practice/list-ops/src/list_ops.clj
@@ -0,0 +1,49 @@
+(ns list-ops)
+
+(defn append 
+  "Given two vectors, it adds all the items in the second vector to the end of the first vector"
+  [coll1 coll2]
+  ;; your code goes here
+)
+
+(defn concatenate 
+  "Given a vector of vectors, it combines all the vectors into one flattened vector"
+  [colls]
+  ;; your code goes here
+)
+
+(defn select-if
+  "Given a predicate and a vector, it returns the vector of all items for which predicate(item) is true"
+  [pred coll]
+  ;; your code goes here
+)
+
+(defn length 
+  "Given a vector, it returns the number of items within it"
+  [coll]
+  ;; your code goes here
+)
+
+(defn apply-to-each 
+  "Given a function and a vector, it returns the vector of the results of applying function(item) on all items"
+  [f coll]
+  ;; your code goes here
+)
+
+(defn foldl 
+  "Given a function, a vector, and initial accumulator, it folds (reduces) each item into the accumulator from the left"
+  [f coll init]
+  ;; your code goes here
+)
+
+(defn foldr
+  "Given a function, a vector, and an initial accumulator, it folds (reduces) each item into the accumulator from the right"
+  [f coll init]
+  ;; your code goes here
+)
+
+(defn reverse-order 
+  "Given a vector, it returns a vector with all the original items, but in reverse order"
+  [coll]
+  ;; your code goes here
+)


Test patch:
diff --git a/exercises/practice/list-ops/.meta/tests.toml b/exercises/practice/list-ops/.meta/tests.toml
new file mode 100644
index 0000000..08b1edc
--- /dev/null
+++ b/exercises/practice/list-ops/.meta/tests.toml
@@ -0,0 +1,106 @@
+# This is an auto-generated file.
+#
+# Regenerating this file via `configlet sync` will:
+# - Recreate every `description` key/value pair
+# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
+# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
+# - Preserve any other key/value pair
+#
+# As user-added comments (using the # character) will be removed when this file
+# is regenerated, comments can be added via a `comment` key.
+
+[485b9452-bf94-40f7-a3db-c3cf4850066a]
+description = "append entries to a list and return the new list -> empty lists"
+
+[2c894696-b609-4569-b149-8672134d340a]
+description = "append entries to a list and return the new list -> list to empty list"
+
+[e842efed-3bf6-4295-b371-4d67a4fdf19c]
+description = "append entries to a list and return the new list -> empty list to list"
+
+[71dcf5eb-73ae-4a0e-b744-a52ee387922f]
+description = "append entries to a list and return the new list -> non-empty lists"
+
+[28444355-201b-4af2-a2f6-5550227bde21]
+description = "concatenate a list of lists -> empty list"
+
+[331451c1-9573-42a1-9869-2d06e3b389a9]
+description = "concatenate a list of lists -> list of lists"
+
+[d6ecd72c-197f-40c3-89a4-aa1f45827e09]
+description = "concatenate a list of lists -> list of nested lists"
+
+[0524fba8-3e0f-4531-ad2b-f7a43da86a16]
+description = "filter list returning only values that satisfy the filter function -> empty list"
+
+[88494bd5-f520-4edb-8631-88e415b62d24]
+description = "filter list returning only values that satisfy the filter function -> non-empty list"
+
+[1cf0b92d-8d96-41d5-9c21-7b3c37cb6aad]
+description = "returns the length of a list -> empty list"
+
+[d7b8d2d9-2d16-44c4-9a19-6e5f237cb71e]
+description = "returns the length of a list -> non-empty list"
+
+[c0bc8962-30e2-4bec-9ae4-668b8ecd75aa]
+description = "return a list of elements whose values equal the list value transformed by the mapping function -> empty list"
+
+[11e71a95-e78b-4909-b8e4-60cdcaec0e91]
+description = "return a list of elements whose values equal the list value transformed by the mapping function -> non-empty list"
+
+[613b20b7-1873-4070-a3a6-70ae5f50d7cc]
+description = "folds (reduces) the given list from the left with a function -> empty list"
+include = false
+
+[e56df3eb-9405-416a-b13a-aabb4c3b5194]
+description = "folds (reduces) the given list from the left with a function -> direction independent function applied to non-empty list"
+include = false
+
+[d2cf5644-aee1-4dfc-9b88-06896676fe27]
+description = "folds (reduces) the given list from the left with a function -> direction dependent function applied to non-empty list"
+include = false
+
+[36549237-f765-4a4c-bfd9-5d3a8f7b07d2]
+description = "folds (reduces) the given list from the left with a function -> empty list"
+reimplements = "613b20b7-1873-4070-a3a6-70ae5f50d7cc"
+
+[7a626a3c-03ec-42bc-9840-53f280e13067]
+description = "folds (reduces) the given list from the left with a function -> direction independent function applied to non-empty list"
+reimplements = "e56df3eb-9405-416a-b13a-aabb4c3b5194"
+
+[d7fcad99-e88e-40e1-a539-4c519681f390]
+description = "folds (reduces) the given list from the left with a function -> direction dependent function applied to non-empty list"
+reimplements = "d2cf5644-aee1-4dfc-9b88-06896676fe27"
+
+[aeb576b9-118e-4a57-a451-db49fac20fdc]
+description = "folds (reduces) the given list from the right with a function -> empty list"
+include = false
+
+[c4b64e58-313e-4c47-9c68-7764964efb8e]
+description = "folds (reduces) the given list from the right with a function -> direction independent function applied to non-empty list"
+include = false
+
+[be396a53-c074-4db3-8dd6-f7ed003cce7c]
+description = "folds (reduces) the given list from the right with a function -> direction dependent function applied to non-empty list"
+include = false
+
+[17214edb-20ba-42fc-bda8-000a5ab525b0]
+description = "folds (reduces) the given list from the right with a function -> empty list"
+reimplements = "aeb576b9-118e-4a57-a451-db49fac20fdc"
+
+[e1c64db7-9253-4a3d-a7c4-5273b9e2a1bd]
+description = "folds (reduces) the given list from the right with a function -> direction independent function applied to non-empty list"
+reimplements = "c4b64e58-313e-4c47-9c68-7764964efb8e"
+
+[8066003b-f2ff-437e-9103-66e6df474844]
+description = "folds (reduces) the given list from the right with a function -> direction dependent function applied to non-empty list"
+reimplements = "be396a53-c074-4db3-8dd6-f7ed003cce7c"
+
+[94231515-050e-4841-943d-d4488ab4ee30]
+description = "reverse the elements of the list -> empty list"
+
+[fcc03d1e-42e0-4712-b689-d54ad761f360]
+description = "reverse the elements of the list -> non-empty list"
+
+[40872990-b5b8-4cb8-9085-d91fc0d05d26]
+description = "reverse the elements of the list -> list of lists is not flattened"
diff --git a/exercises/practice/list-ops/test/list_ops_test.clj b/exercises/practice/list-ops/test/list_ops_test.clj
new file mode 100644
index 0000000..2b26907
--- /dev/null
+++ b/exercises/practice/list-ops/test/list_ops_test.clj
@@ -0,0 +1,63 @@
+(ns list-ops-test
+  (:require [clojure.test :refer [deftest testing is]]
+             list-ops))
+
+(deftest append-test
+  (testing "empty vectors"
+     (is (= [] (list-ops/append [] []))))
+  (testing "vector to empty vector"
+     (is (= [1 2 3 4] (list-ops/append [] [1 2 3 4]))))
+  (testing "empty vector to vector"
+     (is (= [1 2 3 4] (list-ops/append [1 2 3 4] []))))
+  (testing "non-empty vectors"
+     (is (= [1 2 2 3 4 5] (list-ops/append [1 2] [2 3 4 5])))))
+
+(deftest concatenate-test
+  (testing "empty vector"
+     (is (= [] (list-ops/concatenate []))))
+  (testing "vector of vectors"
+     (is (= [1 2 3 4 5 6] (list-ops/concatenate [[1 2] [3] [] [4 5 6]]))))
+  (testing "vector of nested vectors"
+     (is (= [[1] [2] [3] [] [4 5 6]] (list-ops/concatenate [[[1] [2]] [[3]] [[]] [[4 5 6]]])))))
+
+(deftest select-if-test
+  (testing "empty vector"
+     (is (= [] (list-ops/select-if (fn [x] (= (mod x 2) 1)) [])))
+  (testing "non-empty vector"
+     (is (= [1 3 5] (list-ops/select-if (fn [x] (= (mod x 2) 1)) [1 2 3 5]))))))
+
+(deftest length-test
+  (testing "empty vector"
+     (is (= 0 (list-ops/length []))))
+  (testing "non-empty vector"
+     (is (= 4 (list-ops/length [1 2 3 4])))))
+
+(deftest apply-to-each-test
+  (testing "empty vector"
+     (is (= [] (list-ops/apply-to-each (fn [x] (+ x 1)) []))))
+  (testing "non-empty vector"
+     (is (= [2 4 6 8] (list-ops/apply-to-each (fn [x] (+ x 1)) [1 3 5 7])))))
+
+(deftest foldl-test
+  (testing "empty vector"
+     (is (= 2 (list-ops/foldl (fn [acc el] (* el acc)) [] 2))))
+  (testing "direction independent function applied to non-empty vector"
+     (is (= 15 (list-ops/foldl (fn [acc el] (+ el acc)) [1 2 3 4] 5))))
+  (testing "direction dependent function applied to non-empty vector"
+     (is (= 64 (list-ops/foldl (fn [acc el] (/ el acc)) [1 2 3 4] 24)))))
+
+(deftest foldr-test
+  (testing "empty vector"
+     (is (= 2 (list-ops/foldr (fn [acc el] (* el acc)) [] 2))))
+  (testing "direction independent function applied to non-empty vector"
+     (is (= 15 (list-ops/foldr (fn [acc el] (+ el acc)) [1 2 3 4] 5))))
+  (testing "direction dependent function applied to non-empty vector"
+     (is (= 9 (list-ops/foldr (fn [acc el] (/ el acc)) [1 2 3 4] 24)))))
+
+(deftest reverse-order-test
+  (testing "empty vector"
+     (is (= [] (list-ops/reverse-order []))))
+  (testing "non-empty vector"
+     (is (= [7 5 3 1] (list-ops/reverse-order [1 3 5 7]))))
+  (testing "vector of vectors is not flattened"
+     (is (= [[4 5 6] [] [3] [1 2]] (list-ops/reverse-order [[1 2] [3] [] [4 5 6]])))))
diff --git a/exercises/practice/rna-transcription/test/rna_transcription_test.clj b/exercises/practice/rna-transcription/test/rna_transcription_test.clj
index 927a823..eafaf2d 100644
--- a/exercises/practice/rna-transcription/test/rna_transcription_test.clj
+++ b/exercises/practice/rna-transcription/test/rna_transcription_test.clj
@@ -1,21 +1,27 @@
 (ns rna-transcription-test
-  (:require [clojure.test :refer [deftest is]]
+  (:require [clojure.test :refer [deftest testing is]]
             rna-transcription))
 
-(deftest transcribes-cytosine-to-guanine
-  (is (= "G" (rna-transcription/to-rna "C"))))
+(deftest empty-sequence
+  (testing "Empty RNA sequence"
+    (is (= "" (rna-transcription/to-rna "")))))
 
-(deftest transcribes-guanine-to-cytosine
-  (is (= "C" (rna-transcription/to-rna "G"))))
+(deftest rna-complement-of-cytosine
+  (testing "RNA complement of cytosine is guanine"
+    (is (= "G" (rna-transcription/to-rna "C")))))
 
-(deftest transcribes-adenine-to-uracil
-  (is (= "U" (rna-transcription/to-rna "A"))))
+(deftest rna-complement-of-guanine
+  (testing "RNA complement of guanine is cytosine"
+    (is (= "C" (rna-transcription/to-rna "G")))))
 
-(deftest it-transcribes-thymine-to-adenine
-  (is (= "A" (rna-transcription/to-rna "T"))))
+(deftest rna-complement-of-thymine
+  (testing "RNA complement of thymine is adenine"
+    (is (= "A" (rna-transcription/to-rna "T")))))
 
-(deftest it-transcribes-all-nucleotides
-  (is (= "UGCACCAGAAUU" (rna-transcription/to-rna "ACGTGGTCTTAA"))))
+(deftest rna-complement-of-adenine
+  (testing "RNA complement of adenine is uracil"
+    (is (= "U" (rna-transcription/to-rna "A")))))
 
-(deftest it-validates-dna-strands
-  (is (thrown? AssertionError (rna-transcription/to-rna "XCGFGGTDTTAA"))))
+(deftest rna-complement
+  (testing "RNA complement"
+    (is (= "UGCACCAGAAUU" (rna-transcription/to-rna "ACGTGGTCTTAA")))))