Repository: exercism/clojure
Pull request #605 (2024-01-18 18:43:23)

Add missing tests.toml files for accumulate and collate-conjecture
@ErikSchierboom, mark this as `rep/tiny` if you could. These edits don't touch the tests themselves so we shouldn't rerun solutions if possible.

I'll follow up with PRs for several other slugs that do need updates to the tests.


Patch:
diff --git a/.github/workflows/no-important-files-changed.yml b/.github/workflows/no-important-files-changed.yml
new file mode 100644
index 0000000..812e912
--- /dev/null
+++ b/.github/workflows/no-important-files-changed.yml
@@ -0,0 +1,23 @@
+name: No important files changed
+
+on:
+  pull_request_target:
+    types: [opened]
+    branches: [main]
+    paths:
+      - "exercises/concept/**"
+      - "exercises/practice/**"
+      - "!exercises/*/*/.approaches/**"
+      - "!exercises/*/*/.articles/**"
+      - "!exercises/*/*/.docs/**"
+      - "!exercises/*/*/.meta/**"
+
+permissions:
+  pull-requests: write
+
+jobs:
+  check:
+    uses: exercism/github-actions/.github/workflows/check-no-important-files-changed.yml@main
+    with:
+      repository: ${{ github.event.pull_request.head.repo.owner.login }}/${{ github.event.pull_request.head.repo.name }}
+      ref: ${{ github.head_ref }}
diff --git a/.github/workflows/ping-cross-track-maintainers-team.yml b/.github/workflows/ping-cross-track-maintainers-team.yml
new file mode 100644
index 0000000..b6ec9c5
--- /dev/null
+++ b/.github/workflows/ping-cross-track-maintainers-team.yml
@@ -0,0 +1,16 @@
+name: Ping cross-track maintainers team
+
+on:
+  pull_request_target:
+    types:
+      - opened
+
+permissions:
+  pull-requests: write
+
+jobs:
+  ping:
+    if: github.repository_owner == 'exercism' # Stops this job from running on forks
+    uses: exercism/github-actions/.github/workflows/ping-cross-track-maintainers-team.yml@main
+    secrets:
+      github_membership_token: ${{ secrets.COMMUNITY_CONTRIBUTIONS_WORKFLOW_TOKEN }}
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index e9ff73f..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,9 +0,0 @@
----
-language: clojure
-sudo: false
-lein: lein
-jdk: openjdk8
-script:
-- bin/fetch-configlet
-- bin/configlet lint
-- lein test
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index df8e367..3f7813d 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -90,4 +90,4 @@ This policy was initially adopted from the Front-end London Slack community and
 A version history can be seen on [GitHub](https://github.com/exercism/website-copy/edit/main/pages/code_of_conduct.md).
 
 _This policy is a "living" document, and subject to refinement and expansion in the future.
-This policy applies to the Exercism website, the Exercism GitHub organization, any other Exercism-related communication channels (e.g.  Slack, Twitter, email) and any other Exercism entity or event._
+This policy applies to the Exercism website, the Exercism GitHub organization, any other Exercism-related communication channels (e.g. Discord, Forum, Twitter, email) and any other Exercism entity or event._
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index 6cb850b..0000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-version: 1.0.{build}
-install:
-- cmd: >-
-    choco install lein
-build: off
-test_script:
-- cmd: >-
-    cd %APPVEYOR_BUILD_FOLDER%
-
-    lein test
-    
\ No newline at end of file
diff --git a/bin/fetch-configlet b/bin/fetch-configlet
index 4800e15..6bef43a 100755
--- a/bin/fetch-configlet
+++ b/bin/fetch-configlet
@@ -24,10 +24,11 @@ get_download_url() {
   local latest='https://api.github.com/repos/exercism/configlet/releases/latest'
   local arch
   case "$(uname -m)" in
-    x86_64) arch='x86-64' ;;
-    *686*)  arch='i386'   ;;
-    *386*)  arch='i386'   ;;
-    *)      arch='x86-64' ;;
+    aarch64|arm64) arch='arm64'  ;;
+    x86_64)        arch='x86-64' ;;
+    *686*)         arch='i386'   ;;
+    *386*)         arch='i386'   ;;
+    *)             arch='x86-64' ;;
   esac
   local suffix="${os}_${arch}.${ext}"
   curl "${curlopts[@]}" --header 'Accept: application/vnd.github.v3+json' "${latest}" |
@@ -47,7 +48,7 @@ main() {
   fi
 
   local os
-  case "$(uname)" in
+  case "$(uname -s)" in
     Darwin*)   os='macos'   ;;
     Linux*)    os='linux'   ;;
     Windows*)  os='windows' ;;
@@ -58,8 +59,8 @@ main() {
 
   local ext
   case "${os}" in
-    windows*) ext='zip'    ;;
-    *)        ext='tar.gz' ;;
+    windows) ext='zip'    ;;
+    *)       ext='tar.gz' ;;
   esac
 
   echo "Fetching configlet..." >&2
@@ -69,16 +70,16 @@ main() {
   curl "${curlopts[@]}" --output "${output_path}" "${download_url}"
 
   case "${ext}" in
-    *zip) unzip "${output_path}" -d "${output_dir}"   ;;
-    *)    tar xzf "${output_path}" -C "${output_dir}" ;;
+    zip) unzip "${output_path}" -d "${output_dir}"   ;;
+    *)   tar xzf "${output_path}" -C "${output_dir}" ;;
   esac
 
   rm -f "${output_path}"
 
   local executable_ext
   case "${os}" in
-    windows*) executable_ext='.exe' ;;
-    *)        executable_ext=''     ;;
+    windows) executable_ext='.exe' ;;
+    *)       executable_ext=''     ;;
   esac
 
   local configlet_path="${output_dir}/configlet${executable_ext}"
diff --git a/exercises/concept/coordinate-transformation/.docs/instructions.md b/exercises/concept/coordinate-transformation/.docs/instructions.md
index 1b920fa..68a212e 100644
--- a/exercises/concept/coordinate-transformation/.docs/instructions.md
+++ b/exercises/concept/coordinate-transformation/.docs/instructions.md
@@ -48,20 +48,11 @@ Implement the `memoize-transform` function. It takes a function to _memoize_, th
 
 ```clojure
 (def triple-scale (scale2d 3 3))
-(def memoized-scale (memoize-transform triplescale))
+(def memoized-scale (memoize-transform triple-scale))
 
 (memoized-scale 4 3)
 ;; => [12, 9], this is computed since it hasn't been computed before for the arguments
 
 (memoized-scale 4 3)
 ;; => [12, 9], this is remembered, since it was computed already
-
-(def triple-scale (scale2d 3 3))
-(def memoized-scale (memoize-transform triple-scale))
-
-(memoizedScale 4 3)
-;; => [12, 9], this is computed since it hasn't been computed before for the arguments
-
-(memoizedScale 4 3)
-;; => [12, 9], this is remembered, since it was computed already
-```
\ No newline at end of file
+```
diff --git a/exercises/concept/interest-is-interesting/.docs/instructions.md b/exercises/concept/interest-is-interesting/.docs/instructions.md
index fafe03a..d3fe3f1 100644
--- a/exercises/concept/interest-is-interesting/.docs/instructions.md
+++ b/exercises/concept/interest-is-interesting/.docs/instructions.md
@@ -4,8 +4,8 @@ In this exercise you'll be working with savings accounts. Each year, the balance
 
 - -3.213% for a negative balance.
 - 0.5% for a positive balance less than `1000` dollars.
-- 1.621% for a positive balance greater or equal than `1000` dollars and less than `5000` dollars.
-- 2.475% for a positive balance greater or equal than `5000` dollars.
+- 1.621% for a positive balance greater than or equal to `1000` dollars and less than `5000` dollars.
+- 2.475% for a positive balance greater than or equal to `5000` dollars.
 
 Each year, **before** the balance of your savings account is updated, the government allows you to donate a percentage of your money to charity, tax free. Because you're a nice person, if your balance is positive at the end of the year, you donate twice this amount to charities, rounded down to the nearest whole dollar.
 
diff --git a/exercises/practice/all-your-base/.docs/instructions.md b/exercises/practice/all-your-base/.docs/instructions.md
index bce4fd8..1b688b6 100644
--- a/exercises/practice/all-your-base/.docs/instructions.md
+++ b/exercises/practice/all-your-base/.docs/instructions.md
@@ -1,33 +1,28 @@
 # Instructions
 
-Convert a number, represented as a sequence of digits in one base, to any other base.
+Convert a sequence of digits in one base, representing a number, into a sequence of digits in another base, representing the same number.
 
-Implement general base conversion. Given a number in base **a**,
-represented as a sequence of digits, convert it to base **b**.
+~~~~exercism/note
+Try to implement the conversion yourself.
+Do not use something else to perform the conversion for you.
+~~~~
 
-## Note
+## About [Positional Notation][positional-notation]
 
-- Try to implement the conversion yourself.
-  Do not use something else to perform the conversion for you.
-- Your solution should take base **a**, a sequence of digits, and base **b**, in that order.
+In positional notation, a number in base **b** can be understood as a linear combination of powers of **b**.
 
-## About [Positional Notation](https://en.wikipedia.org/wiki/Positional_notation)
+The number 42, _in base 10_, means:
 
-In positional notation, a number in base **b** can be understood as a linear
-combination of powers of **b**.
+`(4 × 10¹) + (2 × 10⁰)`
 
-The number 42, *in base 10*, means:
+The number 101010, _in base 2_, means:
 
-(4 * 10^1) + (2 * 10^0)
+`(1 × 2⁵) + (0 × 2⁴) + (1 × 2³) + (0 × 2²) + (1 × 2¹) + (0 × 2⁰)`
 
-The number 101010, *in base 2*, means:
+The number 1120, _in base 3_, means:
 
-(1 * 2^5) + (0 * 2^4) + (1 * 2^3) + (0 * 2^2) + (1 * 2^1) + (0 * 2^0)
+`(1 × 3³) + (1 × 3²) + (2 × 3¹) + (0 × 3⁰)`
 
-The number 1120, *in base 3*, means:
+_Yes. Those three numbers above are exactly the same. Congratulations!_
 
-(1 * 3^3) + (1 * 3^2) + (2 * 3^1) + (0 * 3^0)
-
-I think you got the idea!
-
-*Yes. Those three numbers above are exactly the same. Congratulations!*
+[positional-notation]: https://en.wikipedia.org/wiki/Positional_notation
diff --git a/exercises/practice/all-your-base/.docs/introduction.md b/exercises/practice/all-your-base/.docs/introduction.md
new file mode 100644
index 0000000..68aaffb
--- /dev/null
+++ b/exercises/practice/all-your-base/.docs/introduction.md
@@ -0,0 +1,8 @@
+# Introduction
+
+You've just been hired as professor of mathematics.
+Your first week went well, but something is off in your second week.
+The problem is that every answer given by your students is wrong!
+Luckily, your math skills have allowed you to identify the problem: the student answers _are_ correct, but they're all in base 2 (binary)!
+Amazingly, it turns out that each week, the students use a different base.
+To help you quickly verify the student answers, you'll be building a tool to translate between bases.
diff --git a/exercises/practice/allergies/.docs/instructions.md b/exercises/practice/allergies/.docs/instructions.md
index b8bbd5a..daf8cfd 100644
--- a/exercises/practice/allergies/.docs/instructions.md
+++ b/exercises/practice/allergies/.docs/instructions.md
@@ -2,20 +2,18 @@
 
 Given a person's allergy score, determine whether or not they're allergic to a given item, and their full list of allergies.
 
-An allergy test produces a single numeric score which contains the
-information about all the allergies the person has (that they were
-tested for).
+An allergy test produces a single numeric score which contains the information about all the allergies the person has (that they were tested for).
 
 The list of items (and their value) that were tested are:
 
-* eggs (1)
-* peanuts (2)
-* shellfish (4)
-* strawberries (8)
-* tomatoes (16)
-* chocolate (32)
-* pollen (64)
-* cats (128)
+- eggs (1)
+- peanuts (2)
+- shellfish (4)
+- strawberries (8)
+- tomatoes (16)
+- chocolate (32)
+- pollen (64)
+- cats (128)
 
 So if Tom is allergic to peanuts and chocolate, he gets a score of 34.
 
@@ -24,7 +22,6 @@ Now, given just that score of 34, your program should be able to say:
 - Whether Tom is allergic to any one of those allergens listed above.
 - All the allergens Tom is allergic to.
 
-Note: a given score may include allergens **not** listed above (i.e.
-allergens that score 256, 512, 1024, etc.).  Your program should
-ignore those components of the score.  For example, if the allergy
-score is 257, your program should only report the eggs (1) allergy.
+Note: a given score may include allergens **not** listed above (i.e. allergens that score 256, 512, 1024, etc.).
+Your program should ignore those components of the score.
+For example, if the allergy score is 257, your program should only report the eggs (1) allergy.
diff --git a/exercises/practice/allergies/.meta/config.json b/exercises/practice/allergies/.meta/config.json
index 8323d0c..a137e05 100644
--- a/exercises/practice/allergies/.meta/config.json
+++ b/exercises/practice/allergies/.meta/config.json
@@ -25,6 +25,6 @@
     ]
   },
   "blurb": "Given a person's allergy score, determine whether or not they're allergic to a given item, and their full list of allergies.",
-  "source": "Jumpstart Lab Warm-up",
-  "source_url": "http://jumpstartlab.com"
+  "source": "Exercise by the JumpstartLab team for students at The Turing School of Software and Design.",
+  "source_url": "https://turing.edu"
 }
diff --git a/exercises/practice/allergies/.meta/src/example.clj b/exercises/practice/allergies/.meta/src/example.clj
index cba06a7..03c7b82 100644
--- a/exercises/practice/allergies/.meta/src/example.clj
+++ b/exercises/practice/allergies/.meta/src/example.clj
@@ -3,22 +3,13 @@
 (def ^:private allergens
   [:eggs :peanuts :shellfish :strawberries :tomatoes :chocolate :pollen :cats])
 
-(defn- flagged?
-  [flags index]
-  (-> (bit-shift-right flags index)
-      (bit-and 1)
-      (pos?)))
-
-(defn allergies
-  "Given an 8-bit bitmap of flags, return the list of matching allergens."
-  [flags]
-  (keep-indexed (fn [index allergen]
-                  (when (flagged? flags index)
-                    allergen))
-                allergens))
+(def ^:private allergen->power
+  (zipmap allergens (range (count allergens))))
 
 (defn allergic-to?
-  "Given an 8-bit bitmap of flags and an allergen, return a boolean
-  indicating whether or not the patient is allergic to the given allergen."
-  [flags allergen]
-  (some #{allergen} (allergies flags)))
+  [score allergen]
+  (bit-test score (allergen->power allergen)))
+
+(defn allergies
+  [score]
+  (keep-indexed #(if (bit-test score %1) %2) allergens))
diff --git a/exercises/practice/anagram/.docs/instructions.md b/exercises/practice/anagram/.docs/instructions.md
index 2675b58..a729848 100644
--- a/exercises/practice/anagram/.docs/instructions.md
+++ b/exercises/practice/anagram/.docs/instructions.md
@@ -1,8 +1,13 @@
 # Instructions
 
-An anagram is a rearrangement of letters to form a new word.
-Given a word and a list of candidates, select the sublist of anagrams of the given word.
+Your task is to, given a target word and a set of candidate words, to find the subset of the candidates that are anagrams of the target.
 
-Given `"listen"` and a list of candidates like `"enlists" "google"
-"inlets" "banana"` the program should return a list containing
-`"inlets"`.
+An anagram is a rearrangement of letters to form a new word: for example `"owns"` is an anagram of `"snow"`.
+A word is _not_ its own anagram: for example, `"stop"` is not an anagram of `"stop"`.
+
+The target and candidates are words of one or more ASCII alphabetic characters (`A`-`Z` and `a`-`z`).
+Lowercase and uppercase characters are equivalent: for example, `"PoTS"` is an anagram of `"sTOp"`, but `StoP` is not an anagram of `sTOp`.
+The anagram set is the subset of the candidate set that are anagrams of the target (in any order).
+Words in the anagram set should have the same letter case as in the candidate set.
+
+Given the target `"stone"` and candidates `"stone"`, `"tones"`, `"banana"`, `"tons"`, `"notes"`, `"Seton"`, the anagram set is `"tones"`, `"notes"`, `"Seton"`.
diff --git a/exercises/practice/anagram/.docs/introduction.md b/exercises/practice/anagram/.docs/introduction.md
new file mode 100644
index 0000000..1acbdf0
--- /dev/null
+++ b/exercises/practice/anagram/.docs/introduction.md
@@ -0,0 +1,12 @@
+# Introduction
+
+At a garage sale, you find a lovely vintage typewriter at a bargain price!
+Excitedly, you rush home, insert a sheet of paper, and start typing away.
+However, your excitement wanes when you examine the output: all words are garbled!
+For example, it prints "stop" instead of "post" and "least" instead of "stale."
+Carefully, you try again, but now it prints "spot" and "slate."
+After some experimentation, you find there is a random delay before each letter is printed, which messes up the order.
+You now understand why they sold it for so little money!
+
+You realize this quirk allows you to generate anagrams, which are words formed by rearranging the letters of another word.
+Pleased with your finding, you spend the rest of the day generating hundreds of anagrams.
diff --git a/exercises/practice/bank-account/.docs/instructions.md b/exercises/practice/bank-account/.docs/instructions.md
index 1265ac8..0955520 100644
--- a/exercises/practice/bank-account/.docs/instructions.md
+++ b/exercises/practice/bank-account/.docs/instructions.md
@@ -1,27 +1,10 @@
 # Instructions
 
-Simulate a bank account supporting opening/closing, withdrawals, and deposits
-of money. Watch out for concurrent transactions!
+Your task is to implement bank accounts supporting opening/closing, withdrawals, and deposits of money.
 
-A bank account can be accessed in multiple ways. Clients can make
-deposits and withdrawals using the internet, mobile phones, etc. Shops
-can charge against the account.
+As bank accounts can be accessed in many different ways (internet, mobile phones, automatic charges), your bank software must allow accounts to be safely accessed from multiple threads/processes (terminology depends on your programming language) in parallel.
+For example, there may be many deposits and withdrawals occurring in parallel; you need to ensure there is no [race conditions][wikipedia] between when you read the account balance and set the new balance.
 
-Create an account that can be accessed from multiple threads/processes
-(terminology depends on your programming language).
+It should be possible to close an account; operations against a closed account must fail.
 
-It should be possible to close an account; operations against a closed
-account must fail.
-
-## Instructions
-
-Run the test file, and fix each of the errors in turn. When you get the
-first test to pass, go to the first pending or skipped test, and make
-that pass as well. When all of the tests are passing, feel free to
-submit.
-
-Remember that passing code is just the first step. The goal is to work
-towards a solution that is as readable and expressive as you can make
-it.
-
-Have fun!
+[wikipedia]: https://en.wikipedia.org/wiki/Race_condition#In_software
diff --git a/exercises/practice/bank-account/.docs/introduction.md b/exercises/practice/bank-account/.docs/introduction.md
new file mode 100644
index 0000000..650b5d9
--- /dev/null
+++ b/exercises/practice/bank-account/.docs/introduction.md
@@ -0,0 +1,20 @@
+# Introduction
+
+After years of filling out forms and waiting, you've finally acquired your banking license.
+This means you are now officially eligible to open your own bank, hurray!
+
+Your first priority is to get the IT systems up and running.
+After a day of hard work, you can already open and close accounts, as well as handle withdrawals and deposits.
+
+Since you couldn't be bothered writing tests, you invite some friends to help test the system.
+However, after just five minutes, one of your friends claims they've lost money!
+While you're confident your code is bug-free, you start looking through the logs to investigate.
+
+Ah yes, just as you suspected, your friend is at fault!
+They shared their test credentials with another friend, and together they conspired to make deposits and withdrawals from the same account _in parallel_.
+Who would do such a thing?
+
+While you argue that it's physically _impossible_ for someone to access their account in parallel, your friend smugly notifies you that the banking rules _require_ you to support this.
+Thus, no parallel banking support, no go-live signal.
+Sighing, you create a mental note to work on this tomorrow.
+This will set your launch date back at _least_ one more day, but well...
diff --git a/exercises/practice/binary-search/.docs/instructions.md b/exercises/practice/binary-search/.docs/instructions.md
index aa1946c..12f4358 100644
--- a/exercises/practice/binary-search/.docs/instructions.md
+++ b/exercises/practice/binary-search/.docs/instructions.md
@@ -11,7 +11,7 @@ Binary search only works when a list has been sorted.
 
 The algorithm looks like this:
 
-- Find the middle element of a *sorted* list and compare it with the item we're looking for.
+- Find the middle element of a _sorted_ list and compare it with the item we're looking for.
 - If the middle element is our item, then we're done!
 - If the middle element is greater than our item, we can eliminate that element and all the elements **after** it.
 - If the middle element is less than our item, we can eliminate that element and all the elements **before** it.
diff --git a/exercises/practice/binary-search/.meta/config.json b/exercises/practice/binary-search/.meta/config.json
index 74b15a0..e2fe40d 100644
--- a/exercises/practice/binary-search/.meta/config.json
+++ b/exercises/practice/binary-search/.meta/config.json
@@ -24,5 +24,5 @@
   },
   "blurb": "Implement a binary search algorithm.",
   "source": "Wikipedia",
-  "source_url": "http://en.wikipedia.org/wiki/Binary_search_algorithm"
+  "source_url": "https://en.wikipedia.org/wiki/Binary_search_algorithm"
 }
diff --git a/exercises/practice/binary-search/.meta/src/example.clj b/exercises/practice/binary-search/.meta/src/example.clj
index 9ea77cf..416b0da 100644
--- a/exercises/practice/binary-search/.meta/src/example.clj
+++ b/exercises/practice/binary-search/.meta/src/example.clj
@@ -1,14 +1,15 @@
 (ns binary-search)
 
-(defn middle [alist]
-  (-> alist (count) (quot 2)))
-
 (defn search-for
-  [elem alist]
-  (let [middle (middle alist)
-        cur-elem (nth alist middle)]
-    (cond
-      (= cur-elem elem) middle
-      (or (= middle (count alist)) (zero? middle)) (throw (Exception. (format "%s not found in list" elem)))
-      (< cur-elem elem) (+ middle (search-for elem (drop middle alist)))
-      (> cur-elem elem) (search-for elem (take middle alist)))))
+  [n coll]
+  (let [coll (vec coll)]
+    (loop [low-idx 0
+           high-idx (dec (count coll))]
+      (if (> low-idx high-idx)
+        (throw (Exception. "not found"))
+        (let [mid-index (quot (+ high-idx low-idx) 2)
+              mid-item (get coll mid-index)]
+          (cond
+            (= n mid-item) mid-index
+            (> mid-item n) (recur low-idx (dec mid-index))
+            :else (recur (inc mid-index) high-idx)))))))
diff --git a/exercises/practice/kindergarten-garden/.docs/instructions.md b/exercises/practice/kindergarten-garden/.docs/instructions.md
index ba89ff9..6fe11a5 100644
--- a/exercises/practice/kindergarten-garden/.docs/instructions.md
+++ b/exercises/practice/kindergarten-garden/.docs/instructions.md
@@ -1,17 +1,21 @@
 # Instructions
 
-Given a diagram, determine which plants each child in the kindergarten class is
-responsible for.
+Your task is to, given a diagram, determine which plants each child in the kindergarten class is responsible for.
 
-The kindergarten class is learning about growing plants. The teacher
-thought it would be a good idea to give them actual seeds, plant them in
-actual dirt, and grow actual plants.
+There are 12 children in the class:
+
+- Alice, Bob, Charlie, David, Eve, Fred, Ginny, Harriet, Ileana, Joseph, Kincaid, and Larry.
+
+Four different types of seeds are planted:
 
-They've chosen to grow grass, clover, radishes, and violets.
+| Plant  | Diagram encoding |
+| ------ | ---------------- |
+| Grass  | G                |
+| Clover | C                |
+| Radish | R                |
+| Violet | V                |
 
-To this end, the children have put little cups along the window sills, and
-planted one type of plant in each cup, choosing randomly from the available
-types of seeds.
+Each child gets four cups, two on each row:
 
 ```text
 [window][window][window]
@@ -19,16 +23,9 @@ types of seeds.
 ........................
 ```
 
-There are 12 children in the class:
-
-- Alice, Bob, Charlie, David,
-- Eve, Fred, Ginny, Harriet,
-- Ileana, Joseph, Kincaid, and Larry.
-
-Each child gets 4 cups, two on each row. Their teacher assigns cups to
-the children alphabetically by their names.
+Their teacher assigns cups to the children alphabetically by their names, which means that Alice comes first and Larry comes last.
 
-The following diagram represents Alice's plants:
+Here is an example diagram representing Alice's plants:
 
 ```text
 [window][window][window]
@@ -36,12 +33,11 @@ VR......................
 RG......................
 ```
 
-In the first row, nearest the windows, she has a violet and a radish.  In the
-second row she has a radish and some grass.
+In the first row, nearest the windows, she has a violet and a radish.
+In the second row she has a radish and some grass.
 
-Your program will be given the plants from left-to-right starting with
-the row nearest the windows. From this, it should be able to determine
-which plants belong to each student.
+Your program will be given the plants from left-to-right starting with the row nearest the windows.
+From this, it should be able to determine which plants belong to each student.
 
 For example, if it's told that the garden looks like so:
 
diff --git a/exercises/practice/kindergarten-garden/.docs/introduction.md b/exercises/practice/kindergarten-garden/.docs/introduction.md
new file mode 100644
index 0000000..5ad97d2
--- /dev/null
+++ b/exercises/practice/kindergarten-garden/.docs/introduction.md
@@ -0,0 +1,6 @@
+# Introduction
+
+The kindergarten class is learning about growing plants.
+The teacher thought it would be a good idea to give the class seeds to plant and grow in the dirt.
+To this end, the children have put little cups along the window sills and planted one type of plant in each cup.
+The children got to pick their favorites from four available types of seeds: grass, clover, radishes, and violets.
diff --git a/exercises/practice/kindergarten-garden/.meta/config.json b/exercises/practice/kindergarten-garden/.meta/config.json
index a5668ab..cea6fa5 100644
--- a/exercises/practice/kindergarten-garden/.meta/config.json
+++ b/exercises/practice/kindergarten-garden/.meta/config.json
@@ -28,6 +28,6 @@
     ]
   },
   "blurb": "Given a diagram, determine which plants each child in the kindergarten class is responsible for.",
-  "source": "Random musings during airplane trip.",
-  "source_url": "http://jumpstartlab.com"
+  "source": "Exercise by the JumpstartLab team for students at The Turing School of Software and Design.",
+  "source_url": "https://turing.edu"
 }
diff --git a/exercises/practice/leap/.approaches/introduction.md b/exercises/practice/leap/.approaches/introduction.md
index caae83e..990fcf4 100644
--- a/exercises/practice/leap/.approaches/introduction.md
+++ b/exercises/practice/leap/.approaches/introduction.md
@@ -69,6 +69,6 @@ This is a little less straight forward approach, but with a little bit of setup
 In the [data shapes approach][data-shapes-approach], we explore how it works.
 
 [modulo]: https://en.wikipedia.org/wiki/Modulo
-[boolean-approach]: https://exercism.org/tracks/clojure/exercises/leap/approaches/boolean
+[boolean-approach]: https://exercism.org/tracks/clojure/exercises/leap/approaches/boolean-operations
 [flow-control-approach]: https://exercism.org/tracks/clojure/exercises/leap/approaches/flow-control
-[data-shapes-approach]: https://exercism.org/tracks/clojure/exercises/leap/approaches/data-shapes
\ No newline at end of file
+[data-shapes-approach]: https://exercism.org/tracks/clojure/exercises/leap/approaches/data-shapes
diff --git a/exercises/practice/leap/.docs/instructions.md b/exercises/practice/leap/.docs/instructions.md
index dc7b4e8..b14f856 100644
--- a/exercises/practice/leap/.docs/instructions.md
+++ b/exercises/practice/leap/.docs/instructions.md
@@ -1,24 +1,3 @@
 # Instructions
 
-Given a year, report if it is a leap year.
-
-The tricky thing here is that a leap year in the Gregorian calendar occurs:
-
-```text
-on every year that is evenly divisible by 4
-  except every year that is evenly divisible by 100
-    unless the year is also evenly divisible by 400
-```
-
-For example, 1997 is not a leap year, but 1996 is.  1900 is not a leap
-year, but 2000 is.
-
-## Notes
-
-Though our exercise adopts some very simple rules, there is more to
-learn!
-
-For a delightful, four minute explanation of the whole leap year
-phenomenon, go watch [this youtube video][video].
-
-[video]: http://www.youtube.com/watch?v=xX96xng7sAE
+Your task is to determine whether a given year is a leap year.
diff --git a/exercises/practice/leap/.docs/introduction.md b/exercises/practice/leap/.docs/introduction.md
new file mode 100644
index 0000000..4ffd2da
--- /dev/null
+++ b/exercises/practice/leap/.docs/introduction.md
@@ -0,0 +1,16 @@
+# Introduction
+
+A leap year (in the Gregorian calendar) occurs:
+
+- In every year that is evenly divisible by 4.
+- Unless the year is evenly divisible by 100, in which case it's only a leap year if the year is also evenly divisible by 400.
+
+Some examples:
+
+- 1997 was not a leap year as it's not divisible by 4.
+- 1900 was not a leap year as it's not divisible by 400.
+- 2000 was a leap year!
+
+~~~~exercism/note
+For a delightful, four-minute explanation of the whole phenomenon of leap years, check out [this YouTube video](https://www.youtube.com/watch?v=xX96xng7sAE).
+~~~~
diff --git a/exercises/practice/leap/.meta/config.json b/exercises/practice/leap/.meta/config.json
index 1e57d3d..653c398 100644
--- a/exercises/practice/leap/.meta/config.json
+++ b/exercises/practice/leap/.meta/config.json
@@ -28,7 +28,7 @@
       ".meta/src/example.clj"
     ]
   },
-  "blurb": "Given a year, report if it is a leap year.",
-  "source": "JavaRanch Cattle Drive, exercise 3",
-  "source_url": "http://www.javaranch.com/leap.jsp"
+  "blurb": "Determine whether a given year is a leap year.",
+  "source": "CodeRanch Cattle Drive, Assignment 3",
+  "source_url": "https://coderanch.com/t/718816/Leap"
 }
diff --git a/exercises/practice/luhn/.docs/instructions.md b/exercises/practice/luhn/.docs/instructions.md
index c7c7d3e..8cbe791 100644
--- a/exercises/practice/luhn/.docs/instructions.md
+++ b/exercises/practice/luhn/.docs/instructions.md
@@ -2,35 +2,31 @@
 
 Given a number determine whether or not it is valid per the Luhn formula.
 
-The [Luhn algorithm](https://en.wikipedia.org/wiki/Luhn_algorithm) is
-a simple checksum formula used to validate a variety of identification
-numbers, such as credit card numbers and Canadian Social Insurance
-Numbers.
+The [Luhn algorithm][luhn] is a simple checksum formula used to validate a variety of identification numbers, such as credit card numbers and Canadian Social Insurance Numbers.
 
 The task is to check if a given string is valid.
 
-Validating a Number
-------
+## Validating a Number
 
-Strings of length 1 or less are not valid. Spaces are allowed in the input,
-but they should be stripped before checking. All other non-digit characters
-are disallowed.
+Strings of length 1 or less are not valid.
+Spaces are allowed in the input, but they should be stripped before checking.
+All other non-digit characters are disallowed.
 
-## Example 1: valid credit card number
+### Example 1: valid credit card number
 
 ```text
 4539 3195 0343 6467
 ```
 
-The first step of the Luhn algorithm is to double every second digit,
-starting from the right. We will be doubling
+The first step of the Luhn algorithm is to double every second digit, starting from the right.
+We will be doubling
 
 ```text
 4_3_ 3_9_ 0_4_ 6_6_
 ```
 
-If doubling the number results in a number greater than 9 then subtract 9
-from the product. The results of our doubling:
+If doubling the number results in a number greater than 9 then subtract 9 from the product.
+The results of our doubling:
 
 ```text
 8569 6195 0383 3437
@@ -42,9 +38,10 @@ Then sum all of the digits:
 8+5+6+9+6+1+9+5+0+3+8+3+3+4+3+7 = 80
 ```
 
-If the sum is evenly divisible by 10, then the number is valid. This number is valid!
+If the sum is evenly divisible by 10, then the number is valid.
+This number is valid!
 
-## Example 2: invalid credit card number
+### Example 2: invalid credit card number
 
 ```text
 8273 1232 7352 0569
@@ -63,3 +60,5 @@ Sum the digits
 ```
 
 57 is not evenly divisible by 10, so this number is not valid.
+
+[luhn]: https://en.wikipedia.org/wiki/Luhn_algorithm
diff --git a/exercises/practice/luhn/.meta/config.json b/exercises/practice/luhn/.meta/config.json
index fcc88e6..c03a923 100644
--- a/exercises/practice/luhn/.meta/config.json
+++ b/exercises/practice/luhn/.meta/config.json
@@ -21,5 +21,5 @@
   },
   "blurb": "Given a number determine whether or not it is valid per the Luhn formula.",
   "source": "The Luhn Algorithm on Wikipedia",
-  "source_url": "http://en.wikipedia.org/wiki/Luhn_algorithm"
+  "source_url": "https://en.wikipedia.org/wiki/Luhn_algorithm"
 }
diff --git a/exercises/practice/luhn/.meta/src/example.clj b/exercises/practice/luhn/.meta/src/example.clj
index c6b4521..4290395 100644
--- a/exercises/practice/luhn/.meta/src/example.clj
+++ b/exercises/practice/luhn/.meta/src/example.clj
@@ -1,34 +1,47 @@
-(ns luhn
-  (:require [clojure.string :as string]))
-
-(defn to-reversed-digits
-  "returns a lazy sequence of least to most significant digits of n"
-  [n]
-  (->> [n 0]
-       (iterate (fn [[i _]] [(quot i 10) (mod i 10)]))
-       (take-while (complement #{[0 0]}))
-       (map second)
-       rest))
-
-(defn checksum
-  "returns the luhn checksum of n, assuming it has a check digit"
-  [n]
-  (-> (->> n
-           to-reversed-digits
-           (map * (cycle [1 2]))
-           (map #(if (>= % 10) (- % 9) %))
-           (apply +))
-      (mod 10)))
-
-(defn string->long
-  "Strips any non-digit characters and converts the string into a Long"
-  [n]
-  (-> n (string/replace #"[^0-9]+" "") Long/parseLong))
+(ns luhn)
+
+(defn string->digits
+  [s]
+  (map #(Character/digit ^char % 10) s))
+
+(defn valid-integer?
+  [digits]
+  (and (> (count digits) 1)
+       (every? #(<= 0 % 9) digits)))
+
+(defn zero-pad
+  [digits]
+  (if (even? (count digits))
+    digits
+    (conj digits 0)))
+
+(defn transform-digit
+  [index val]
+  (if (odd? index)
+    val
+    (let [dval (* 2 val)]
+      (if (> dval 9)
+        (- dval 9)
+        dval))))
+
+(defn get-luhn-value
+  [digits]
+  (let [padded-digits (zero-pad digits)]
+    (->> padded-digits
+         (map-indexed transform-digit)
+         (reduce +))))
+
+(defn valid-luhn-value?
+  [val]
+  (int? (/ val 10)))
+
+(defn remove-spaces
+  [s]
+  (remove #{\space} s))
 
 (defn valid?
-  "whether n has a valid luhn check-digit"
-  [n]
-  ; Numbers with non digit/whitespace or only 1 digit are invalid
-  (if (or (re-find #"[^0-9\s]+" n) (>= 1 (count (string/trim n))))
-    false
-    (zero? (-> n string->long checksum))))
+  "Returns true if the given string represents a valid luhn number, else false"
+  [s]
+  (let [digits (string->digits (remove-spaces s))]
+    (and (valid-integer? digits)
+         (valid-luhn-value? (get-luhn-value digits)))))
diff --git a/exercises/practice/meetup/.docs/instructions.md b/exercises/practice/meetup/.docs/instructions.md
index fe1a9c2..000de2f 100644
--- a/exercises/practice/meetup/.docs/instructions.md
+++ b/exercises/practice/meetup/.docs/instructions.md
@@ -1,27 +1,34 @@
 # Instructions
 
-Calculate the date of meetups.
+Your task is to find the exact date of a meetup, given a month, year, weekday and week.
 
-Typically meetups happen on the same day of the week.  In this exercise, you
-will take a description of a meetup date, and return the actual meetup date.
+There are five week values to consider: `first`, `second`, `third`, `fourth`, `last`, `teenth`.
 
-Examples of general descriptions are:
+For example, you might be asked to find the date for the meetup on the first Monday in January 2018 (January 1, 2018).
 
-- The first Monday of January 2017
-- The third Tuesday of January 2017
-- The wednesteenth of January 2017
-- The last Thursday of January 2017
+Similarly, you might be asked to find:
 
-The descriptors you are expected to parse are:
-first, second, third, fourth, fifth, last, monteenth, tuesteenth, wednesteenth,
-thursteenth, friteenth, saturteenth, sunteenth
+- the third Tuesday of August 2019 (August 20, 2019)
+- the teenth Wednesday of May 2020 (May 13, 2020)
+- the fourth Sunday of July 2021 (July 25, 2021)
+- the last Thursday of November 2022 (November 24, 2022)
+- the teenth Saturday of August 1953 (August 15, 1953)
 
-Note that "monteenth", "tuesteenth", etc are all made up words. There was a
-meetup whose members realized that there are exactly 7 numbered days in a month
-that end in '-teenth'. Therefore, one is guaranteed that each day of the week
-(Monday, Tuesday, ...) will have exactly one date that is named with '-teenth'
-in every month.
+## Teenth
 
-Given examples of a meetup dates, each containing a month, day, year, and
-descriptor calculate the date of the actual meetup.  For example, if given
-"The first Monday of January 2017", the correct meetup date is 2017/1/2.
+The teenth week refers to the seven days in a month that end in '-teenth' (13th, 14th, 15th, 16th, 17th, 18th and 19th).
+
+If asked to find the teenth Saturday of August, 1953, we check its calendar:
+
+```plaintext
+    August 1953
+Su Mo Tu We Th Fr Sa
+                   1
+ 2  3  4  5  6  7  8
+ 9 10 11 12 13 14 15
+16 17 18 19 20 21 22
+23 24 25 26 27 28 29
+30 31
+```
+
+From this we find that the teenth Saturday is August 15, 1953.
diff --git a/exercises/practice/meetup/.docs/introduction.md b/exercises/practice/meetup/.docs/introduction.md
new file mode 100644
index 0000000..29170ef
--- /dev/null
+++ b/exercises/practice/meetup/.docs/introduction.md
@@ -0,0 +1,29 @@
+# Introduction
+
+Every month, your partner meets up with their best friend.
+Both of them have very busy schedules, making it challenging to find a suitable date!
+Given your own busy schedule, your partner always double-checks potential meetup dates with you:
+
+- "Can I meet up on the first Friday of next month?"
+- "What about the third Wednesday?"
+- "Maybe the last Sunday?"
+
+In this month's call, your partner asked you this question:
+
+- "I'd like to meet up on the teenth Thursday; is that okay?"
+
+Confused, you ask what a "teenth" day is.
+Your partner explains that a teenth day, a concept they made up, refers to the days in a month that end in '-teenth':
+
+- 13th (thirteenth)
+- 14th (fourteenth)
+- 15th (fifteenth)
+- 16th (sixteenth)
+- 17th (seventeenth)
+- 18th (eighteenth)
+- 19th (nineteenth)
+
+As there are also seven weekdays, it is guaranteed that each day of the week has _exactly one_ teenth day each month.
+
+Now that you understand the concept of a teenth day, you check your calendar.
+You don't have anything planned on the teenth Thursday, so you happily confirm the date with your partner.
diff --git a/exercises/practice/meetup/.meta/config.json b/exercises/practice/meetup/.meta/config.json
index 240a6a3..c21c0b7 100644
--- a/exercises/practice/meetup/.meta/config.json
+++ b/exercises/practice/meetup/.meta/config.json
@@ -26,6 +26,5 @@
     ]
   },
   "blurb": "Calculate the date of meetups.",
-  "source": "Jeremy Hinegardner mentioned a Boulder meetup that happens on the Wednesteenth of every month",
-  "source_url": "https://twitter.com/copiousfreetime"
+  "source": "Jeremy Hinegardner mentioned a Boulder meetup that happens on the Wednesteenth of every month"
 }
diff --git a/exercises/practice/minesweeper/.docs/instructions.md b/exercises/practice/minesweeper/.docs/instructions.md
index 1114cc9..7c1df2e 100644
--- a/exercises/practice/minesweeper/.docs/instructions.md
+++ b/exercises/practice/minesweeper/.docs/instructions.md
@@ -1,35 +1,24 @@
 # Instructions
 
-Add the mine counts to a completed Minesweeper board.
+Your task is to add the mine counts to empty squares in a completed Minesweeper board.
+The board itself is a rectangle composed of squares that are either empty (`' '`) or a mine (`'*'`).
 
-Minesweeper is a popular game where the user has to find the mines using
-numeric hints that indicate how many mines are directly adjacent
-(horizontally, vertically, diagonally) to a square.
+For each empty square, count the number of mines adjacent to it (horizontally, vertically, diagonally).
+If the empty square has no adjacent mines, leave it empty.
+Otherwise replace it with the adjacent mines count.
 
-In this exercise you have to create some code that counts the number of
-mines adjacent to a given empty square and replaces that square with the
-count.
+For example, you may receive a 5 x 4 board like this (empty spaces are represented here with the '·' character for display on screen):
 
-The board is a rectangle composed of blank space (' ') characters. A mine
-is represented by an asterisk ('\*') character.
-
-If a given space has no adjacent mines at all, leave that square blank.
-
-## Examples
-
-For example you may receive a 5 x 4 board like this (empty spaces are
-represented here with the '·' character for display on screen):
-
-```
+```text
 ·*·*·
 ··*··
 ··*··
 ·····
 ```
 
-And your code will transform it into this:
+Which your code should transform into this:
 
-```
+```text
 1*3*1
 13*31
 ·2*2·
diff --git a/exercises/practice/minesweeper/.docs/introduction.md b/exercises/practice/minesweeper/.docs/introduction.md
new file mode 100644
index 0000000..5f74a74
--- /dev/null
+++ b/exercises/practice/minesweeper/.docs/introduction.md
@@ -0,0 +1,5 @@
+# Introduction
+
+[Minesweeper][wikipedia] is a popular game where the user has to find the mines using numeric hints that indicate how many mines are directly adjacent (horizontally, vertically, diagonally) to a square.
+
+[wikipedia]: https://en.wikipedia.org/wiki/Minesweeper_(video_game)
diff --git a/exercises/practice/minesweeper/.meta/config.json b/exercises/practice/minesweeper/.meta/config.json
index 1646fe3..0ee7c9c 100644
--- a/exercises/practice/minesweeper/.meta/config.json
+++ b/exercises/practice/minesweeper/.meta/config.json
@@ -17,5 +17,5 @@
       ".meta/src/example.clj"
     ]
   },
-  "blurb": "Add the numbers to a minesweeper board"
+  "blurb": "Add the numbers to a minesweeper board."
 }
diff --git a/exercises/practice/pig-latin/.docs/instructions.md b/exercises/practice/pig-latin/.docs/instructions.md
index bcb1251..a9645ac 100644
--- a/exercises/practice/pig-latin/.docs/instructions.md
+++ b/exercises/practice/pig-latin/.docs/instructions.md
@@ -1,18 +1,46 @@
 # Instructions
 
-Implement a program that translates from English to Pig Latin.
+Your task is to translate text from English to Pig Latin.
+The translation is defined using four rules, which look at the pattern of vowels and consonants at the beginning of a word.
+These rules look at each word's use of vowels and consonants:
 
-Pig Latin is a made-up children's language that's intended to be
-confusing. It obeys a few simple rules (below), but when it's spoken
-quickly it's really difficult for non-children (and non-native speakers)
-to understand.
+- vowels: the letters `a`, `e`, `i`, `o`, and `u`
+- consonants: the other 21 letters of the English alphabet
 
-- **Rule 1**: If a word begins with a vowel sound, add an "ay" sound to the end of the word. Please note that "xr" and "yt" at the beginning of a word make vowel sounds (e.g. "xray" -> "xrayay", "yttria" -> "yttriaay").
-- **Rule 2**: If a word begins with a consonant sound, move it to the end of the word and then add an "ay" sound to the end of the word. Consonant sounds can be made up of multiple consonants, a.k.a. a consonant cluster (e.g. "chair" -> "airchay").
-- **Rule 3**: If a word starts with a consonant sound followed by "qu", move it to the end of the word, and then add an "ay" sound to the end of the word (e.g. "square" -> "aresquay").
-- **Rule 4**: If a word contains a "y" after a consonant cluster or as the second letter in a two letter word it makes a vowel sound (e.g. "rhythm" -> "ythmrhay", "my" -> "ymay").
+## Rule 1
 
-There are a few more rules for edge cases, and there are regional
-variants too.
+If a word begins with a vowel, or starts with `"xr"` or `"yt"`, add an `"ay"` sound to the end of the word.
 
-See <http://en.wikipedia.org/wiki/Pig_latin> for more details.
+For example:
+
+- `"apple"` -> `"appleay"` (starts with vowel)
+- `"xray"` -> `"xrayay"` (starts with `"xr"`)
+- `"yttria"` -> `"yttriaay"` (starts with `"yt"`)
+
+## Rule 2
+
+If a word begins with one or more consonants, first move those consonants to the end of the word and then add an `"ay"` sound to the end of the word.
+
+For example:
+
+- `"pig"` -> `"igp"` -> `"igpay"` (starts with single consonant)
+- `"chair"` -> `"airch"` -> `"airchay"` (starts with multiple consonants)
+- `"thrush"` -> `"ushthr"` -> `"ushthray"` (starts with multiple consonants)
+
+## Rule 3
+
+If a word starts with zero or more consonants followed by `"qu"`, first move those consonants (if any) and the `"qu"` part to the end of the word, and then add an `"ay"` sound to the end of the word.
+
+For example:
+
+- `"quick"` -> `"ickqu"` -> `"ickquay"` (starts with `"qu"`, no preceding consonants)
+- `"square"` -> `"aresqu"` -> `"aresquay"` (starts with one consonant followed by `"qu`")
+
+## Rule 4
+
+If a word starts with one or more consonants followed by `"y"`, first move the consonants preceding the `"y"`to the end of the word, and then add an `"ay"` sound to the end of the word.
+
+Some examples:
+
+- `"my"` -> `"ym"` -> `"ymay"` (starts with single consonant followed by `"y"`)
+- `"rhythm"` -> `"ythmrh"` -> `"ythmrhay"` (starts with multiple consonants followed by `"y"`)
diff --git a/exercises/practice/pig-latin/.docs/introduction.md b/exercises/practice/pig-latin/.docs/introduction.md
new file mode 100644
index 0000000..04baa47
--- /dev/null
+++ b/exercises/practice/pig-latin/.docs/introduction.md
@@ -0,0 +1,8 @@
+# Introduction
+
+Your parents have challenged you and your sibling to a game of two-on-two basketball.
+Confident they'll win, they let you score the first couple of points, but then start taking over the game.
+Needing a little boost, you start speaking in [Pig Latin][pig-latin], which is a made-up children's language that's difficult for non-children to understand.
+This will give you the edge to prevail over your parents!
+
+[pig-latin]: https://en.wikipedia.org/wiki/Pig_latin
diff --git a/exercises/practice/pig-latin/.meta/config.json b/exercises/practice/pig-latin/.meta/config.json
index 628df60..2133c3d 100644
--- a/exercises/practice/pig-latin/.meta/config.json
+++ b/exercises/practice/pig-latin/.meta/config.json
@@ -20,7 +20,7 @@
       ".meta/src/example.clj"
     ]
   },
-  "blurb": "Implement a program that translates from English to Pig Latin",
+  "blurb": "Implement a program that translates from English to Pig Latin.",
   "source": "The Pig Latin exercise at Test First Teaching by Ultrasaurus",
   "source_url": "https://github.com/ultrasaurus/test-first-teaching/blob/master/learn_ruby/pig_latin/"
 }
diff --git a/exercises/practice/protein-translation/.docs/instructions.md b/exercises/practice/protein-translation/.docs/instructions.md
index c211345..7dc34d2 100644
--- a/exercises/practice/protein-translation/.docs/instructions.md
+++ b/exercises/practice/protein-translation/.docs/instructions.md
@@ -11,7 +11,8 @@ Codons: `"AUG", "UUU", "UCU"`
 
 Protein: `"Methionine", "Phenylalanine", "Serine"`
 
-There are 64 codons which in turn correspond to 20 amino acids; however, all of the codon sequences and resulting amino acids are not important in this exercise.  If it works for one codon, the program should work for all of them.
+There are 64 codons which in turn correspond to 20 amino acids; however, all of the codon sequences and resulting amino acids are not important in this exercise.
+If it works for one codon, the program should work for all of them.
 However, feel free to expand the list in the test suite to include them all.
 
 There are also three terminating codons (also known as 'STOP' codons); if any of these codons are encountered (by the ribosome), all translation ends and the protein is terminated.
@@ -28,15 +29,17 @@ Note the stop codon `"UAA"` terminates the translation and the final methionine
 
 Below are the codons and resulting Amino Acids needed for the exercise.
 
-Codon                 | Protein
-:---                  | :---
-AUG                   | Methionine
-UUU, UUC              | Phenylalanine
-UUA, UUG              | Leucine
-UCU, UCC, UCA, UCG    | Serine
-UAU, UAC              | Tyrosine
-UGU, UGC              | Cysteine
-UGG                   | Tryptophan
-UAA, UAG, UGA         | STOP
-
-Learn more about [protein translation on Wikipedia](http://en.wikipedia.org/wiki/Translation_(biology))
+| Codon              | Protein       |
+| :----------------- | :------------ |
+| AUG                | Methionine    |
+| UUU, UUC           | Phenylalanine |
+| UUA, UUG           | Leucine       |
+| UCU, UCC, UCA, UCG | Serine        |
+| UAU, UAC           | Tyrosine      |
+| UGU, UGC           | Cysteine      |
+| UGG                | Tryptophan    |
+| UAA, UAG, UGA      | STOP          |
+
+Learn more about [protein translation on Wikipedia][protein-translation].
+
+[protein-translation]: https://en.wikipedia.org/wiki/Translation_(biology)
diff --git a/exercises/practice/raindrops/.docs/instructions.md b/exercises/practice/raindrops/.docs/instructions.md
index a78585d..df64410 100644
--- a/exercises/practice/raindrops/.docs/instructions.md
+++ b/exercises/practice/raindrops/.docs/instructions.md
@@ -1,16 +1,24 @@
 # Instructions
 
-Your task is to convert a number into a string that contains raindrop sounds corresponding to certain potential factors. A factor is a number that evenly divides into another number, leaving no remainder. The simplest way to test if a one number is a factor of another is to use the [modulo operation](https://en.wikipedia.org/wiki/Modulo_operation).
+Your task is to convert a number into its corresponding raindrop sounds.
 
-The rules of `raindrops` are that if a given number:
+If a given number:
 
-- has 3 as a factor, add 'Pling' to the result.
-- has 5 as a factor, add 'Plang' to the result.
-- has 7 as a factor, add 'Plong' to the result.
-- _does not_ have any of 3, 5, or 7 as a factor, the result should be the digits of the number.
+- is divisible by 3, add "Pling" to the result.
+- is divisible by 5, add "Plang" to the result.
+- is divisible by 7, add "Plong" to the result.
+- **is not** divisible by 3, 5, or 7, the result should be the number as a string.
 
 ## Examples
 
-- 28 has 7 as a factor, but not 3 or 5, so the result would be "Plong".
-- 30 has both 3 and 5 as factors, but not 7, so the result would be "PlingPlang".
-- 34 is not factored by 3, 5, or 7, so the result would be "34".
+- 28 is divisible by 7, but not 3 or 5, so the result would be `"Plong"`.
+- 30 is divisible by 3 and 5, but not 7, so the result would be `"PlingPlang"`.
+- 34 is not divisible by 3, 5, or 7, so the result would be `"34"`.
+
+~~~~exercism/note
+A common way to test if one number is evenly divisible by another is to compare the [remainder][remainder] or [modulus][modulo] to zero.
+Most languages provide operators or functions for one (or both) of these.
+
+[remainder]: https://exercism.org/docs/programming/operators/remainder
+[modulo]: https://en.wikipedia.org/wiki/Modulo_operation
+~~~~
diff --git a/exercises/practice/raindrops/.docs/introduction.md b/exercises/practice/raindrops/.docs/introduction.md
new file mode 100644
index 0000000..ba12100
--- /dev/null
+++ b/exercises/practice/raindrops/.docs/introduction.md
@@ -0,0 +1,3 @@
+# Introduction
+
+Raindrops is a slightly more complex version of the FizzBuzz challenge, a classic interview question.
diff --git a/exercises/practice/reverse-string/.approaches/config.json b/exercises/practice/reverse-string/.approaches/config.json
new file mode 100644
index 0000000..6316f3d
--- /dev/null
+++ b/exercises/practice/reverse-string/.approaches/config.json
@@ -0,0 +1,36 @@
+{
+  "introduction": {
+    "authors": [
+      "michalporeba"
+    ]
+  },
+  "approaches": [
+    {
+      "uuid": "0ec50f64-a8f7-49b5-b0d0-3977149c23ba",
+      "slug": "string-builder",
+      "title": "StringBuilder",
+      "blurb": "Use the StringBuilder class.",
+      "authors": [
+        "michalporeba"
+      ]
+    },
+    {
+      "uuid": "9cc22152-82c3-49b1-95a7-8ee62cdeac21",
+      "slug": "sequence",
+      "title": "Use the fact that a string is also a sequence",
+      "blurb": "Reverse the string as a sequence.",
+      "authors": [
+        "michalporeba"
+      ]
+    },
+    {
+      "uuid": "533b4795-7f1f-4048-b022-9cc500eebe62",
+      "slug": "recursion",
+      "title": "Recursion",
+      "blurb": "Reverse the string recursively.",
+      "authors": [
+        "michalporeba"
+      ]
+    }
+  ]
+}
diff --git a/exercises/practice/reverse-string/.approaches/introduction.md b/exercises/practice/reverse-string/.approaches/introduction.md
new file mode 100644
index 0000000..0b160f6
--- /dev/null
+++ b/exercises/practice/reverse-string/.approaches/introduction.md
@@ -0,0 +1,53 @@
+# Introduction
+
+[Strings][string] in Clojure are immutable, which means we cannot reverse them in place.
+Instead, typically, we will create a new string while reversing the original one.
+
+## String builder
+
+One way to work around it is to use a string builder from the underlying Java Virtual Machine.
+
+```clojure
+(defn reverse-string [s]
+  (.toString
+    (.reverse
+      (StringBuilder. s))))
+```
+
+Let's look at the [string builder approach][string-builder-approach] and a shortcut to it.
+
+## It's a sequence
+
+Beyond the above, there are a great many different solutions, but in general, they depend on two facts.
+[Strings][string] in Clojure are Java [string][java-string]s.
+Many core Clojure functions call `seq` on their arguments automatically converting a string into a sequence of characters.
+And there are many ways to reverse a sequence.
+
+```clojure
+(defn reverse-string [s]
+  (apply str (reverse s)))
+```
+
+We discuss some variations in [It's a sequence approach][sequence-approach].
+
+## Recursion
+
+A distinct variation of the above is to process a sequence in a recursive function.
+
+```clojure
+(defn reverse-string [s]
+  (loop [s s acc ""]
+    (if (empty? s)
+      acc
+      (recur
+        (rest s)
+        (str (first s) acc)))))
+```
+
+Let's explore the [recursive approach][recursive-approach].
+
+[string]: https://clojure-doc.org/articles/cookbooks/strings
+[java-string]: https://docs.oracle.com/javase/8/docs/api/java/lang/String.html
+[string-builder-approach]: https://exercism.org/tracks/clojure/exercises/reverse-string/approaches/string-builder
+[sequence-approach]: https://exercism.org/tracks/clojure/exercises/reverse-string/approaches/its-a-sequence
+[recursive-approach]: https://exercism.org/tracks/clojure/exercises/reverse-string/approaches/recursion
\ No newline at end of file
diff --git a/exercises/practice/reverse-string/.approaches/recursion/content.md b/exercises/practice/reverse-string/.approaches/recursion/content.md
new file mode 100644
index 0000000..94691ec
--- /dev/null
+++ b/exercises/practice/reverse-string/.approaches/recursion/content.md
@@ -0,0 +1,28 @@
+# Recursion
+
+```clojure
+(defn reverse-string [s]
+  (loop [s s acc ""]
+    (if (empty? s)
+      acc
+      (recur
+        (rest s)
+        (str (first s) acc)))))
+```
+
+## Performance considerations
+
+It is not necessarily bad to use recursion as shown in the example above.
+In fact, many other approaches use recursion behind the scenes as, for instance, `clojure.string/join` is implemented using recursion.
+However, we should remember that strings are immutable, so code like the above will be inefficient.
+Instead, we should consider using the `StringBuilder` in the recursive function. For example, like so:
+
+```clojure
+(defn reverse-string [s]
+  (loop [s (into () s) sb (StringBuilder. "")]
+    (if (empty? s)
+      (.toString sb)
+      (recur
+        (rest s)
+        (-> sb (.append (first s)))))))
+```
\ No newline at end of file
diff --git a/exercises/practice/reverse-string/.approaches/recursion/snippet.txt b/exercises/practice/reverse-string/.approaches/recursion/snippet.txt
new file mode 100644
index 0000000..4e17596
--- /dev/null
+++ b/exercises/practice/reverse-string/.approaches/recursion/snippet.txt
@@ -0,0 +1,7 @@
+(defn reverse-string [s]
+  (loop [s s acc ""]
+    (if (empty? s)
+      acc
+      (recur
+        (rest s)
+        (str (first s) acc)))))
diff --git a/exercises/practice/reverse-string/.approaches/sequence/content.md b/exercises/practice/reverse-string/.approaches/sequence/content.md
new file mode 100644
index 0000000..84e39be
--- /dev/null
+++ b/exercises/practice/reverse-string/.approaches/sequence/content.md
@@ -0,0 +1,61 @@
+# It's a sequence
+
+```clojure
+(defn reverse-string [string]
+  (apply str (reverse string)))
+```
+
+In Clojure, many functions that operate on sequences will automaticaly convert a string parameter into a sequence of characters.
+This is because many core functions call `seq` on its arguments.
+Also, ["most of Clojure's core library treats collections and sequences the same way"][collections-and-sequences].
+It follows that we can use any method to reverse a sequence or a collection to reverse a string.
+
+There will be three stops in this group of approaches:
+
+1. Convert a string to a sequence or a collection. (Is usually implicit, part of the next step).
+2. Reverse the sequence or a collection.
+3. Convert the sequence of characters back into a string. (It has to be explicit).
+
+## Reversing
+
+Here are a few options for reversing a sequence of characters.
+```clojure
+(reverse s)
+```
+The above is self-explanatory.
+```clojure
+(into () s)
+```
+This takes one character at a time from `s` and adds it to a sequence.
+Because in Clojure, by default, new elements are added to the beginning of the list,
+`into` reverses the characters at the same time as changing a string into an explicit sequence.
+
+The more explicit verbose version of this operation could be something like this:
+```clojure
+(reduce conj () s)
+```
+
+## Converting back to a string
+
+There are many options here, too.
+
+```clojure
+(apply str (reverse s))
+(reduce str (reverse s))
+(clojure.string/join (reverse s))
+```
+
+## A single step version
+
+We also have an option to combine all three operations into a single function call:
+
+```clojure
+(defn reverse-string [s]
+  (reduce #(str %2 %1) "" s))
+```
+
+## Which one to use?
+
+I'd suggest using the one that is the most readable to you.
+
+[collections-and-sequences]: https://clojure-doc.org/articles/language/collections_and_sequences/
\ No newline at end of file
diff --git a/exercises/practice/reverse-string/.approaches/sequence/snippet.txt b/exercises/practice/reverse-string/.approaches/sequence/snippet.txt
new file mode 100644
index 0000000..b39884d
--- /dev/null
+++ b/exercises/practice/reverse-string/.approaches/sequence/snippet.txt
@@ -0,0 +1,2 @@
+(defn reverse-string [string]
+  (apply str (reverse string)))
diff --git a/exercises/practice/reverse-string/.approaches/string-builder/content.md b/exercises/practice/reverse-string/.approaches/string-builder/content.md
new file mode 100644
index 0000000..6c3df76
--- /dev/null
+++ b/exercises/practice/reverse-string/.approaches/string-builder/content.md
@@ -0,0 +1,46 @@
+# String builder
+
+```clojure
+(defn reverse-string [s]
+  (.toString
+    (.reverse
+      (StringBuilder. s))))
+```
+
+In Clojure, as in Java, strings are immutable.
+It means that with every change we want to make to any string, we create a new string in memory.
+
+String recreation can be very resource-intensive, especially when new strings are created in many steps.
+This is a common problem, so Java provides the `StringBuilder` class, which holds characters as a collection,
+allowing for modifications until we are ready to create the string by calling `toString()` method.
+
+String builder has a built-in `reverse()` method, which we can use as shown above.
+The complete approach is to initialise a `StringBuilder` with the string we want to reverse.
+Then reverse it, and finally convert it back to string.
+
+## The shortcut
+
+Is accessing the underlying Java Virtual Machine and Java classes necessary?
+Couldn't we just use the `clojure.string/reverse` function instead?
+
+We could! This is the alternative way to reverse a string:
+
+```clojure
+(defn reverse-string [s]
+  (clojure.string/reverse s))
+```
+
+In fact, at some level, we could consider both approaches to be equivalent.
+Let's have a look at [the implementation of the `clojure.string/reverse`][implementation] function.
+
+```clojure
+(defn ^String reverse
+  "Returns s with its characters reversed."
+  {:added "1.2"}
+  [^CharSequence s]
+  (.toString (.reverse (StringBuilder. s))))
+```
+
+While there is a little bit more going on in the syntax, at its core, it is the same as the code at the top of this approach.
+
+[implementation]: https://github.com/clojure/clojure/blob/08a2d9bdd013143a87e50fa82e9740e2ab4ee2c2/src/clj/clojure/string.clj#L48C3-L48C3
diff --git a/exercises/practice/reverse-string/.approaches/string-builder/snippet.txt b/exercises/practice/reverse-string/.approaches/string-builder/snippet.txt
new file mode 100644
index 0000000..e98cefb
--- /dev/null
+++ b/exercises/practice/reverse-string/.approaches/string-builder/snippet.txt
@@ -0,0 +1,4 @@
+(defn reverse-string [s]
+  (.toString
+    (.reverse
+      (StringBuilder. s))))
diff --git a/exercises/practice/reverse-string/.docs/instructions.md b/exercises/practice/reverse-string/.docs/instructions.md
index 039ee33..0ff4198 100644
--- a/exercises/practice/reverse-string/.docs/instructions.md
+++ b/exercises/practice/reverse-string/.docs/instructions.md
@@ -1,7 +1,9 @@
 # Instructions
 
-Reverse a string
+Your task is to reverse a given string.
 
-For example:
-input: "cool"
-output: "looc"
+Some examples:
+
+- Turn `"stressed"` into `"desserts"`.
+- Turn `"strops"` into `"sports"`.
+- Turn `"racecar"` into `"racecar"`.
diff --git a/exercises/practice/reverse-string/.docs/introduction.md b/exercises/practice/reverse-string/.docs/introduction.md
new file mode 100644
index 0000000..02233e0
--- /dev/null
+++ b/exercises/practice/reverse-string/.docs/introduction.md
@@ -0,0 +1,5 @@
+# Introduction
+
+Reversing strings (reading them from right to left, rather than from left to right) is a surprisingly common task in programming.
+
+For example, in bioinformatics, reversing the sequence of DNA or RNA strings is often important for various analyses, such as finding complementary strands or identifying palindromic sequences that have biological significance.
diff --git a/exercises/practice/reverse-string/.meta/config.json b/exercises/practice/reverse-string/.meta/config.json
index f5666a0..63b57c5 100644
--- a/exercises/practice/reverse-string/.meta/config.json
+++ b/exercises/practice/reverse-string/.meta/config.json
@@ -19,7 +19,7 @@
       ".meta/src/example.clj"
     ]
   },
-  "blurb": "Reverse a string.",
+  "blurb": "Reverse a given string.",
   "source": "Introductory challenge to reverse an input string",
   "source_url": "https://medium.freecodecamp.org/how-to-reverse-a-string-in-javascript-in-3-different-ways-75e4763c68cb"
 }
diff --git a/exercises/practice/roman-numerals/.docs/instructions.md b/exercises/practice/roman-numerals/.docs/instructions.md
index ce25f20..50e2f5b 100644
--- a/exercises/practice/roman-numerals/.docs/instructions.md
+++ b/exercises/practice/roman-numerals/.docs/instructions.md
@@ -1,43 +1,12 @@
-# Instructions
+# Introduction
 
-Write a function to convert from normal numbers to Roman Numerals.
+Your task is to convert a number from Arabic numerals to Roman numerals.
 
-The Romans were a clever bunch. They conquered most of Europe and ruled
-it for hundreds of years. They invented concrete and straight roads and
-even bikinis. One thing they never discovered though was the number
-zero. This made writing and dating extensive histories of their exploits
-slightly more challenging, but the system of numbers they came up with
-is still in use today. For example the BBC uses Roman numerals to date
-their programmes.
+For this exercise, we are only concerned about traditional Roman numerals, in which the largest number is MMMCMXCIX (or 3,999).
 
-The Romans wrote numbers using letters - I, V, X, L, C, D, M. (notice
-these letters have lots of straight lines and are hence easy to hack
-into stone tablets).
+~~~~exercism/note
+There are lots of different ways to convert between Arabic and Roman numerals.
+We recommend taking a naive approach first to familiarise yourself with the concept of Roman numerals and then search for more efficient methods.
 
-```text
- 1  => I
-10  => X
- 7  => VII
-```
-
-There is no need to be able to convert numbers larger than about 3000.
-(The Romans themselves didn't tend to go any higher)
-
-Wikipedia says: Modern Roman numerals ... are written by expressing each
-digit separately starting with the left most digit and skipping any
-digit with a value of zero.
-
-To see this in practice, consider the example of 1990.
-
-In Roman numerals 1990 is MCMXC:
-
-1000=M
-900=CM
-90=XC
-
-2008 is written as MMVIII:
-
-2000=MM
-8=VIII
-
-See also: http://www.novaroma.org/via_romana/numbers.html
+Make sure to check out our Deep Dive video at the end to explore the different approaches you can take!
+~~~~
diff --git a/exercises/practice/roman-numerals/.docs/introduction.md b/exercises/practice/roman-numerals/.docs/introduction.md
new file mode 100644
index 0000000..6fd942f
--- /dev/null
+++ b/exercises/practice/roman-numerals/.docs/introduction.md
@@ -0,0 +1,59 @@
+# Description
+
+Today, most people in the world use Arabic numerals (0–9).
+But if you travelled back two thousand years, you'd find that most Europeans were using Roman numerals instead.
+
+To write a Roman numeral we use the following Latin letters, each of which has a value:
+
+| M    | D   | C   | L   | X   | V   | I   |
+| ---- | --- | --- | --- | --- | --- | --- |
+| 1000 | 500 | 100 | 50  | 10  | 5   | 1   |
+
+A Roman numeral is a sequence of these letters, and its value is the sum of the letters' values.
+For example, `XVIII` has the value 18 (`10 + 5 + 1 + 1 + 1 = 18`).
+
+There's one rule that makes things trickier though, and that's that **the same letter cannot be used more than three times in succession**.
+That means that we can't express numbers such as 4 with the seemingly natural `IIII`.
+Instead, for those numbers, we use a subtraction method between two letters.
+So we think of `4` not as `1 + 1 + 1 + 1` but instead as `5 - 1`.
+And slightly confusingly to our modern thinking, we write the smaller number first.
+This applies only in the following cases: 4 (`IV`), 9 (`IX`), 40 (`XL`), 90 (`XC`), 400 (`CD`) and 900 (`CM`).
+
+Order matters in Roman numerals!
+Letters (and the special compounds above) must be ordered by decreasing value from left to right.
+
+Here are some examples:
+
+```text
+ 105 => CV
+---- => --
+ 100 => C
++  5 =>  V
+```
+
+```text
+ 106 => CVI
+---- => --
+ 100 => C
++  5 =>  V
++  1 =>   I
+```
+
+```text
+ 104 => CIV
+---- => ---
+ 100 => C
++  4 =>  IV
+```
+
+And a final more complex example:
+
+```text
+ 1996 => MCMXCVI
+----- => -------
+ 1000 => M
++ 900 =>  CM
++  90 =>    XC
++   5 =>      V
++   1 =>       I
+```
diff --git a/exercises/practice/roman-numerals/.meta/config.json b/exercises/practice/roman-numerals/.meta/config.json
index 4210276..ebaf454 100644
--- a/exercises/practice/roman-numerals/.meta/config.json
+++ b/exercises/practice/roman-numerals/.meta/config.json
@@ -24,7 +24,7 @@
       ".meta/src/example.clj"
     ]
   },
-  "blurb": "Write a function to convert from normal numbers to Roman Numerals.",
+  "blurb": "Convert modern Arabic numbers into Roman numerals.",
   "source": "The Roman Numeral Kata",
   "source_url": "https://codingdojo.org/kata/RomanNumerals/"
 }
diff --git a/exercises/practice/scrabble-score/.docs/instructions.md b/exercises/practice/scrabble-score/.docs/instructions.md
index 3f986c9..738f928 100644
--- a/exercises/practice/scrabble-score/.docs/instructions.md
+++ b/exercises/practice/scrabble-score/.docs/instructions.md
@@ -1,40 +1,25 @@
 # Instructions
 
-Given a word, compute the Scrabble score for that word.
+Your task is to compute a word's Scrabble score by summing the values of its letters.
 
-## Letter Values
+The letters are valued as follows:
 
-You'll need these:
+| Letter                       | Value |
+| ---------------------------- | ----- |
+| A, E, I, O, U, L, N, R, S, T | 1     |
+| D, G                         | 2     |
+| B, C, M, P                   | 3     |
+| F, H, V, W, Y                | 4     |
+| K                            | 5     |
+| J, X                         | 8     |
+| Q, Z                         | 10    |
 
-```text
-Letter                           Value
-A, E, I, O, U, L, N, R, S, T       1
-D, G                               2
-B, C, M, P                         3
-F, H, V, W, Y                      4
-K                                  5
-J, X                               8
-Q, Z                               10
-```
-
-## Examples
-
-"cabbage" should be scored as worth 14 points:
+For example, the word "cabbage" is worth 14 points:
 
 - 3 points for C
-- 1 point for A, twice
-- 3 points for B, twice
+- 1 point for A
+- 3 points for B
+- 3 points for B
+- 1 point for A
 - 2 points for G
 - 1 point for E
-
-And to total:
-
-- `3 + 2*1 + 2*3 + 2 + 1`
-- = `3 + 2 + 6 + 3`
-- = `5 + 9`
-- = 14
-
-## Extensions
-
-- You can play a double or a triple letter.
-- You can play a double or a triple word.
diff --git a/exercises/practice/scrabble-score/.docs/introduction.md b/exercises/practice/scrabble-score/.docs/introduction.md
new file mode 100644
index 0000000..8821f24
--- /dev/null
+++ b/exercises/practice/scrabble-score/.docs/introduction.md
@@ -0,0 +1,7 @@
+# Introduction
+
+[Scrabble][wikipedia] is a word game where players place letter tiles on a board to form words.
+Each letter has a value.
+A word's score is the sum of its letters' values.
+
+[wikipedia]: https://en.wikipedia.org/wiki/Scrabble
diff --git a/exercises/practice/sieve/.docs/instructions.md b/exercises/practice/sieve/.docs/instructions.md
index 3adf1d5..085c0a5 100644
--- a/exercises/practice/sieve/.docs/instructions.md
+++ b/exercises/practice/sieve/.docs/instructions.md
@@ -1,28 +1,42 @@
 # Instructions
 
-Your task is to create a program that implements the Sieve of Eratosthenes algorithm to find prime numbers.
+Your task is to create a program that implements the Sieve of Eratosthenes algorithm to find all prime numbers less than or equal to a given number.
 
-A prime number is a number that is only divisible by 1 and itself.
+A prime number is a number larger than 1 that is only divisible by 1 and itself.
 For example, 2, 3, 5, 7, 11, and 13 are prime numbers.
-
-The Sieve of Eratosthenes is an ancient algorithm that works by taking a list of numbers and crossing out all the numbers that aren't prime.
-
-A number that is **not** prime is called a "composite number".
+By contrast, 6 is _not_ a prime number as it not only divisible by 1 and itself, but also by 2 and 3.
 
 To use the Sieve of Eratosthenes, you first create a list of all the numbers between 2 and your given number.
 Then you repeat the following steps:
 
-1. Find the next unmarked number in your list. This is a prime number.
-2. Mark all the multiples of that prime number as composite (not prime).
+1. Find the next unmarked number in your list (skipping over marked numbers).
+   This is a prime number.
+2. Mark all the multiples of that prime number as **not** prime.
 
 You keep repeating these steps until you've gone through every number in your list.
 At the end, all the unmarked numbers are prime.
 
 ~~~~exercism/note
-[Wikipedia's Sieve of Eratosthenes article][eratosthenes] has a useful graphic that explains the algorithm.
-
 The tests don't check that you've implemented the algorithm, only that you've come up with the correct list of primes.
-A good first test is to check that you do not use division or remainder operations.
-
-[eratosthenes]: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
+To check you are implementing the Sieve correctly, a good first test is to check that you do not use division or remainder operations.
 ~~~~
+
+## Example
+
+Let's say you're finding the primes less than or equal to 10.
+
+- List out 2, 3, 4, 5, 6, 7, 8, 9, 10, leaving them all unmarked.
+- 2 is unmarked and is therefore a prime.
+  Mark 4, 6, 8 and 10 as "not prime".
+- 3 is unmarked and is therefore a prime.
+  Mark 6 and 9 as not prime _(marking 6 is optional - as it's already been marked)_.
+- 4 is marked as "not prime", so we skip over it.
+- 5 is unmarked and is therefore a prime.
+  Mark 10 as not prime _(optional - as it's already been marked)_.
+- 6 is marked as "not prime", so we skip over it.
+- 7 is unmarked and is therefore a prime.
+- 8 is marked as "not prime", so we skip over it.
+- 9 is marked as "not prime", so we skip over it.
+- 10 is marked as "not prime", so we stop as there are no more numbers to check.
+
+You've examined all numbers and found 2, 3, 5, and 7 are still unmarked, which means they're the primes less than or equal to 10.
diff --git a/exercises/practice/sieve/.meta/config.json b/exercises/practice/sieve/.meta/config.json
index 551d8d7..2a4ce8c 100644
--- a/exercises/practice/sieve/.meta/config.json
+++ b/exercises/practice/sieve/.meta/config.json
@@ -22,5 +22,5 @@
   },
   "blurb": "Use the Sieve of Eratosthenes to find all the primes from 2 up to a given number.",
   "source": "Sieve of Eratosthenes at Wikipedia",
-  "source_url": "http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes"
+  "source_url": "https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes"
 }
diff --git a/exercises/practice/space-age/.docs/instructions.md b/exercises/practice/space-age/.docs/instructions.md
index 19cca8b..f23b5e2 100644
--- a/exercises/practice/space-age/.docs/instructions.md
+++ b/exercises/practice/space-age/.docs/instructions.md
@@ -1,18 +1,28 @@
 # Instructions
 
-Given an age in seconds, calculate how old someone would be on:
+Given an age in seconds, calculate how old someone would be on a planet in our Solar System.
 
-   - Mercury: orbital period 0.2408467 Earth years
-   - Venus: orbital period 0.61519726 Earth years
-   - Earth: orbital period 1.0 Earth years, 365.25 Earth days, or 31557600 seconds
-   - Mars: orbital period 1.8808158 Earth years
-   - Jupiter: orbital period 11.862615 Earth years
-   - Saturn: orbital period 29.447498 Earth years
-   - Uranus: orbital period 84.016846 Earth years
-   - Neptune: orbital period 164.79132 Earth years
+One Earth year equals 365.25 Earth days, or 31,557,600 seconds.
+If you were told someone was 1,000,000,000 seconds old, their age would be 31.69 Earth-years.
 
-So if you were told someone were 1,000,000,000 seconds old, you should
-be able to say that they're 31.69 Earth-years old.
+For the other planets, you have to account for their orbital period in Earth Years:
 
-If you're wondering why Pluto didn't make the cut, go watch [this
-youtube video](http://www.youtube.com/watch?v=Z_2gbGXzFbs).
+| Planet  | Orbital period in Earth Years |
+| ------- | ----------------------------- |
+| Mercury | 0.2408467                     |
+| Venus   | 0.61519726                    |
+| Earth   | 1.0                           |
+| Mars    | 1.8808158                     |
+| Jupiter | 11.862615                     |
+| Saturn  | 29.447498                     |
+| Uranus  | 84.016846                     |
+| Neptune | 164.79132                     |
+
+~~~~exercism/note
+The actual length of one complete orbit of the Earth around the sun is closer to 365.256 days (1 sidereal year).
+The Gregorian calendar has, on average, 365.2425 days.
+While not entirely accurate, 365.25 is the value used in this exercise.
+See [Year on Wikipedia][year] for more ways to measure a year.
+
+[year]: https://en.wikipedia.org/wiki/Year#Summary
+~~~~
diff --git a/exercises/practice/space-age/.docs/introduction.md b/exercises/practice/space-age/.docs/introduction.md
new file mode 100644
index 0000000..014d788
--- /dev/null
+++ b/exercises/practice/space-age/.docs/introduction.md
@@ -0,0 +1,20 @@
+# Introduction
+
+The year is 2525 and you've just embarked on a journey to visit all planets in the Solar System (Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus and Neptune).
+The first stop is Mercury, where customs require you to fill out a form (bureaucracy is apparently _not_ Earth-specific).
+As you hand over the form to the customs officer, they scrutinize it and frown.
+"Do you _really_ expect me to believe you're just 50 years old?
+You must be closer to 200 years old!"
+
+Amused, you wait for the customs officer to start laughing, but they appear to be dead serious.
+You realize that you've entered your age in _Earth years_, but the officer expected it in _Mercury years_!
+As Mercury's orbital period around the sun is significantly shorter than Earth, you're actually a lot older in Mercury years.
+After some quick calculations, you're able to provide your age in Mercury Years.
+The customs officer smiles, satisfied, and waves you through.
+You make a mental note to pre-calculate your planet-specific age _before_ future customs checks, to avoid such mix-ups.
+
+~~~~exercism/note
+If you're wondering why Pluto didn't make the cut, go watch [this YouTube video][pluto-video].
+
+[pluto-video]: https://www.youtube.com/watch?v=Z_2gbGXzFbs
+~~~~
diff --git a/exercises/practice/space-age/.meta/config.json b/exercises/practice/space-age/.meta/config.json
index 3e52d17..7294c9c 100644
--- a/exercises/practice/space-age/.meta/config.json
+++ b/exercises/practice/space-age/.meta/config.json
@@ -26,5 +26,5 @@
   },
   "blurb": "Given an age in seconds, calculate how old someone is in terms of a given planet's solar years.",
   "source": "Partially inspired by Chapter 1 in Chris Pine's online Learn to Program tutorial.",
-  "source_url": "http://pine.fm/LearnToProgram/?Chapter=01"
+  "source_url": "https://pine.fm/LearnToProgram/?Chapter=01"
 }
diff --git a/exercises/practice/spiral-matrix/.docs/instructions.md b/exercises/practice/spiral-matrix/.docs/instructions.md
index af412dd..01e8a77 100644
--- a/exercises/practice/spiral-matrix/.docs/instructions.md
+++ b/exercises/practice/spiral-matrix/.docs/instructions.md
@@ -1,12 +1,11 @@
 # Instructions
 
-Given the size, return a square matrix of numbers in spiral order.
+Your task is to return a square matrix of a given size.
 
-The matrix should be filled with natural numbers, starting from 1
-in the top-left corner, increasing in an inward, clockwise spiral order,
-like these examples:
+The matrix should be filled with natural numbers, starting from 1 in the top-left corner, increasing in an inward, clockwise spiral order, like these examples:
 
 ## Examples
+
 ### Spiral matrix of size 3
 
 ```text
diff --git a/exercises/practice/spiral-matrix/.docs/introduction.md b/exercises/practice/spiral-matrix/.docs/introduction.md
new file mode 100644
index 0000000..25c7eb5
--- /dev/null
+++ b/exercises/practice/spiral-matrix/.docs/introduction.md
@@ -0,0 +1,11 @@
+# Introduction
+
+In a small village near an ancient forest, there was a legend of a hidden treasure buried deep within the woods.
+Despite numerous attempts, no one had ever succeeded in finding it.
+This was about to change, however, thanks to a young explorer named Elara.
+She had discovered an old document containing instructions on how to locate the treasure.
+Using these instructions, Elara was able to draw a map that revealed the path to the treasure.
+
+To her surprise, the path followed a peculiar clockwise spiral.
+It was no wonder no one had been able to find the treasure before!
+With the map in hand, Elara embarks on her journey to uncover the hidden treasure.
diff --git a/exercises/practice/spiral-matrix/.meta/config.json b/exercises/practice/spiral-matrix/.meta/config.json
index 1ba77d1..2780de6 100644
--- a/exercises/practice/spiral-matrix/.meta/config.json
+++ b/exercises/practice/spiral-matrix/.meta/config.json
@@ -16,7 +16,7 @@
       ".meta/src/example.clj"
     ]
   },
-  "blurb": " Given the size, return a square matrix of numbers in spiral order.",
+  "blurb": "Given the size, return a square matrix of numbers in spiral order.",
   "source": "Reddit r/dailyprogrammer challenge #320 [Easy] Spiral Ascension.",
-  "source_url": "https://www.reddit.com/r/dailyprogrammer/comments/6i60lr/20170619_challenge_320_easy_spiral_ascension/"
+  "source_url": "https://web.archive.org/web/20230607064729/https://old.reddit.com/r/dailyprogrammer/comments/6i60lr/20170619_challenge_320_easy_spiral_ascension/"
 }
diff --git a/exercises/practice/two-fer/.docs/instructions.md b/exercises/practice/two-fer/.docs/instructions.md
index a9bb4a3..adc5348 100644
--- a/exercises/practice/two-fer/.docs/instructions.md
+++ b/exercises/practice/two-fer/.docs/instructions.md
@@ -2,14 +2,13 @@
 
 Your task is to determine what you will say as you give away the extra cookie.
 
-If your friend likes cookies, and is named Do-yun, then you will say:
+If you know the person's name (e.g. if they're named Do-yun), then you will say:
 
 ```text
 One for Do-yun, one for me.
 ```
 
-If your friend doesn't like cookies, you give the cookie to the next person in line at the bakery.
-Since you don't know their name, you will say _you_ instead.
+If you don't know the person's name, you will say _you_ instead.
 
 ```text
 One for you, one for me.
@@ -17,9 +16,9 @@ One for you, one for me.
 
 Here are some examples:
 
-|Name    |Dialogue
-|:-------|:------------------
-|Alice   |One for Alice, one for me.
-|Bohdan  |One for Bohdan, one for me.
-|        |One for you, one for me.
-|Zaphod  |One for Zaphod, one for me.
+| Name   | Dialogue                    |
+| :----- | :-------------------------- |
+| Alice  | One for Alice, one for me.  |
+| Bohdan | One for Bohdan, one for me. |
+|        | One for you, one for me.    |
+| Zaphod | One for Zaphod, one for me. |
diff --git a/exercises/practice/two-fer/.docs/introduction.md b/exercises/practice/two-fer/.docs/introduction.md
index 8c12439..5947a22 100644
--- a/exercises/practice/two-fer/.docs/introduction.md
+++ b/exercises/practice/two-fer/.docs/introduction.md
@@ -5,4 +5,4 @@ Two-for-one is a way of saying that if you buy one, you also get one for free.
 So the phrase "two-fer" often implies a two-for-one offer.
 
 Imagine a bakery that has a holiday offer where you can buy two cookies for the price of one ("two-fer one!").
-You go for the offer and (very generously) decide to give the extra cookie to a friend.
+You take the offer and (very generously) decide to give the extra cookie to someone else in the queue.
diff --git a/exercises/practice/two-fer/.meta/config.json b/exercises/practice/two-fer/.meta/config.json
index 1364a56..8110c51 100644
--- a/exercises/practice/two-fer/.meta/config.json
+++ b/exercises/practice/two-fer/.meta/config.json
@@ -18,6 +18,6 @@
       ".meta/src/example.clj"
     ]
   },
-  "blurb": "Create a sentence of the form \"One for X, one for me.\"",
+  "blurb": "Create a sentence of the form \"One for X, one for me.\".",
   "source_url": "https://github.com/exercism/problem-specifications/issues/757"
 }
diff --git a/exercises/practice/yacht/.docs/instructions.md b/exercises/practice/yacht/.docs/instructions.md
index d6e119e..519b7a6 100644
--- a/exercises/practice/yacht/.docs/instructions.md
+++ b/exercises/practice/yacht/.docs/instructions.md
@@ -1,35 +1,30 @@
-# Description
+# Instructions
 
-The dice game [Yacht][yacht] is from the same family as Poker Dice, Generala and particularly Yahtzee, of which it is a precursor.
-In the game, five dice are rolled and the result can be entered in any of twelve categories.
-The score of a throw of the dice depends on category chosen.
+Given five dice and a category, calculate the score of the dice for that category.
 
-## Scores in Yacht
-
-| Category | Score | Description | Example |
-| -------- | ----- | ----------- | ------- |
-| Ones | 1 × number of ones | Any combination | 1 1 1 4 5 scores 3 |
-| Twos | 2 × number of twos | Any combination | 2 2 3 4 5 scores 4 |
-| Threes | 3 × number of threes | Any combination | 3 3 3 3 3 scores 15 |
-| Fours | 4 × number of fours | Any combination | 1 2 3 3 5 scores 0 |
-| Fives | 5 × number of fives| Any combination | 5 1 5 2 5 scores 15 |
-| Sixes | 6 × number of sixes | Any combination | 2 3 4 5 6 scores 6 |
-| Full House | Total of the dice | Three of one number and two of another | 3 3 3 5 5 scores 19 |
-| Four of a Kind | Total of the four dice | At least four dice showing the same face | 4 4 4 4 6 scores 16 |
-| Little Straight |  30 points | 1-2-3-4-5 | 1 2 3 4 5 scores 30 |
-| Big Straight | 30 points | 2-3-4-5-6 | 2 3 4 5 6 scores 30 |
-| Choice | Sum of the dice | Any combination | 2 3 3 4 6 scores 18 |
-| Yacht | 50 points | All five dice showing the same face | 4 4 4 4 4 scores 50 |
+~~~~exercism/note
+You'll always be presented with five dice.
+Each dice's value will be between one and six inclusively.
+The dice may be unordered.
+~~~~
 
-If the dice do not satisfy the requirements of a category, the score is zero.
-If, for example, *Four Of A Kind* is entered in the *Yacht* category, zero points are scored.
-A *Yacht* scores zero if entered in the *Full House* category.
-
-## Task
+## Scores in Yacht
 
-Given a list of values for five dice and a category, your solution should return the score of the dice for that category.
-If the dice do not satisfy the requirements of the category your solution should return 0.
-You can assume that five values will always be presented, and the value of each will be between one and six inclusively.
-You should not assume that the dice are ordered.
+| Category        | Score                  | Description                              | Example             |
+| --------------- | ---------------------- | ---------------------------------------- | ------------------- |
+| Ones            | 1 × number of ones     | Any combination                          | 1 1 1 4 5 scores 3  |
+| Twos            | 2 × number of twos     | Any combination                          | 2 2 3 4 5 scores 4  |
+| Threes          | 3 × number of threes   | Any combination                          | 3 3 3 3 3 scores 15 |
+| Fours           | 4 × number of fours    | Any combination                          | 1 2 3 3 5 scores 0  |
+| Fives           | 5 × number of fives    | Any combination                          | 5 1 5 2 5 scores 15 |
+| Sixes           | 6 × number of sixes    | Any combination                          | 2 3 4 5 6 scores 6  |
+| Full House      | Total of the dice      | Three of one number and two of another   | 3 3 3 5 5 scores 19 |
+| Four of a Kind  | Total of the four dice | At least four dice showing the same face | 4 4 4 4 6 scores 16 |
+| Little Straight | 30 points              | 1-2-3-4-5                                | 1 2 3 4 5 scores 30 |
+| Big Straight    | 30 points              | 2-3-4-5-6                                | 2 3 4 5 6 scores 30 |
+| Choice          | Sum of the dice        | Any combination                          | 2 3 3 4 6 scores 18 |
+| Yacht           | 50 points              | All five dice showing the same face      | 4 4 4 4 4 scores 50 |
 
-[yacht]: https://en.wikipedia.org/wiki/Yacht_(dice_game)
+If the dice do **not** satisfy the requirements of a category, the score is zero.
+If, for example, _Four Of A Kind_ is entered in the _Yacht_ category, zero points are scored.
+A _Yacht_ scores zero if entered in the _Full House_ category.
diff --git a/exercises/practice/yacht/.docs/introduction.md b/exercises/practice/yacht/.docs/introduction.md
new file mode 100644
index 0000000..5b541f5
--- /dev/null
+++ b/exercises/practice/yacht/.docs/introduction.md
@@ -0,0 +1,11 @@
+# Introduction
+
+Each year, something new is "all the rage" in your high school.
+This year it is a dice game: [Yacht][yacht].
+
+The game of Yacht is from the same family as Poker Dice, Generala and particularly Yahtzee, of which it is a precursor.
+The game consists of twelve rounds.
+In each, five dice are rolled and the player chooses one of twelve categories.
+The chosen category is then used to score the throw of the dice.
+
+[yacht]: https://en.wikipedia.org/wiki/Yacht_(dice_game)
diff --git a/exercises/practice/yacht/.meta/config.json b/exercises/practice/yacht/.meta/config.json
index 85a212a..59e5d64 100644
--- a/exercises/practice/yacht/.meta/config.json
+++ b/exercises/practice/yacht/.meta/config.json
@@ -1,21 +1,20 @@
 {
-    "authors": [
-      "porkostomus"
+  "authors": [
+    "porkostomus"
+  ],
+  "contributors": [],
+  "files": {
+    "solution": [
+      "src/yacht.clj"
     ],
-    "contributors": [],
-    "files": {
-      "solution": [
-        "src/yacht.clj"
-      ],
-      "test": [
-        "test/yacht_test.clj"
-      ],
-      "example": [
-        ".meta/src/example.clj"
-      ]
-    },
-    "blurb": "Score a single throw of dice in the game Yacht",
-    "source": "James Kilfiger, using wikipedia",
-    "source_url": "https://en.wikipedia.org/wiki/Yacht_(dice_game)"
-  }
-  
\ No newline at end of file
+    "test": [
+      "test/yacht_test.clj"
+    ],
+    "example": [
+      ".meta/src/example.clj"
+    ]
+  },
+  "blurb": "Score a single throw of dice in the game Yacht.",
+  "source": "James Kilfiger, using wikipedia",
+  "source_url": "https://en.wikipedia.org/wiki/Yacht_(dice_game)"
+}


Test patch:
diff --git a/exercises/concept/cars-assemble/test/cars_assemble_test.clj b/exercises/concept/cars-assemble/test/cars_assemble_test.clj
index 1eeec2e..0cce00a 100644
--- a/exercises/concept/cars-assemble/test/cars_assemble_test.clj
+++ b/exercises/concept/cars-assemble/test/cars_assemble_test.clj
@@ -2,29 +2,33 @@
   (:require cars-assemble
             [clojure.test :refer [deftest is testing]]))
 
+(defn- float-equal?
+  [x y]
+  (= (float x) (float y)))
+
 (deftest ^{:task 1} production-rate-speed-0-test
   (testing "Production rate for speed 0"
-    (is (= 0.0 (cars-assemble/production-rate 0)))))
+    (is (float-equal? 0.0 (cars-assemble/production-rate 0)))))
 
 (deftest ^{:task 1} production-rate-speed-1-test
   (testing "Production rate for speed 1"
-    (is (= 221.0 (cars-assemble/production-rate 1)))))
+    (is (float-equal? 221.0 (cars-assemble/production-rate 1)))))
 
 (deftest ^{:task 1} production-rate-speed-4-test
   (testing "Production rate for speed 4"
-    (is (= 884.0 (cars-assemble/production-rate 4)))))
+    (is (float-equal? 884.0 (cars-assemble/production-rate 4)))))
 
 (deftest ^{:task 1} production-rate-speed-7-test
   (testing "Production rate for speed 7"
-    (is (= 1392.3 (cars-assemble/production-rate 7)))))
+    (is (float-equal? 1392.3 (cars-assemble/production-rate 7)))))
 
 (deftest ^{:task 1} production-rate-speed-9-test
   (testing "Production rate for speed 9"
-    (is (= 1591.2 (cars-assemble/production-rate 9)))))
+    (is (float-equal? 1591.2 (cars-assemble/production-rate 9)))))
 
 (deftest ^{:task 1} production-rate-speed-10-test
   (testing "Production rate for speed 10"
-    (is (= 1701.7 (cars-assemble/production-rate 10)))))
+    (is (float-equal? 1701.7 (cars-assemble/production-rate 10)))))
 
 (deftest ^{:task 2} working-items-speed-0-test
   (testing "Working items for speed 0"
diff --git a/exercises/practice/accumulate/.meta/tests.toml b/exercises/practice/accumulate/.meta/tests.toml
new file mode 100644
index 0000000..d7858e0
--- /dev/null
+++ b/exercises/practice/accumulate/.meta/tests.toml
@@ -0,0 +1,30 @@
+# 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.
+
+[64d97c14-36dd-44a8-9621-2cecebd6ed23]
+description = "accumulate empty"
+
+[00008ed2-4651-4929-8c08-8b4dbd70872e]
+description = "accumulate squares"
+
+[551016da-4396-4cae-b0ec-4c3a1a264125]
+description = "accumulate upcases"
+
+[cdf95597-b6ec-4eac-a838-3480d13d0d05]
+description = "accumulate reversed strings"
+
+[bee8e9b6-b16f-4cd2-be3b-ccf7457e50bb]
+description = "accumulate recursively"
+include = false
+
+[0b357334-4cad-49e1-a741-425202edfc7c]
+description = "accumulate recursively"
+reimplements = "bee8e9b6-b16f-4cd2-be3b-ccf7457e50bb"
diff --git a/exercises/practice/allergies/.meta/tests.toml b/exercises/practice/allergies/.meta/tests.toml
index 8a754c2..799ab85 100644
--- a/exercises/practice/allergies/.meta/tests.toml
+++ b/exercises/practice/allergies/.meta/tests.toml
@@ -1,150 +1,160 @@
-# This is an auto-generated file. Regular comments will be removed when this
-# file is regenerated. Regenerating will not touch any manually added keys,
-# so comments can be added in a "comment" key.
+# 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.
 
 [17fc7296-2440-4ac4-ad7b-d07c321bc5a0]
-description = "not allergic to anything"
+description = "testing for eggs allergy -> not allergic to anything"
 
 [07ced27b-1da5-4c2e-8ae2-cb2791437546]
-description = "allergic only to eggs"
+description = "testing for eggs allergy -> allergic only to eggs"
 
 [5035b954-b6fa-4b9b-a487-dae69d8c5f96]
-description = "allergic to eggs and something else"
+description = "testing for eggs allergy -> allergic to eggs and something else"
 
 [64a6a83a-5723-4b5b-a896-663307403310]
-description = "allergic to something, but not eggs"
+description = "testing for eggs allergy -> allergic to something, but not eggs"
 
 [90c8f484-456b-41c4-82ba-2d08d93231c6]
-description = "allergic to everything"
+description = "testing for eggs allergy -> allergic to everything"
 
 [d266a59a-fccc-413b-ac53-d57cb1f0db9d]
-description = "not allergic to anything"
+description = "testing for peanuts allergy -> not allergic to anything"
 
 [ea210a98-860d-46b2-a5bf-50d8995b3f2a]
-description = "allergic only to peanuts"
+description = "testing for peanuts allergy -> allergic only to peanuts"
 
 [eac69ae9-8d14-4291-ac4b-7fd2c73d3a5b]
-description = "allergic to peanuts and something else"
+description = "testing for peanuts allergy -> allergic to peanuts and something else"
 
 [9152058c-ce39-4b16-9b1d-283ec6d25085]
-description = "allergic to something, but not peanuts"
+description = "testing for peanuts allergy -> allergic to something, but not peanuts"
 
 [d2d71fd8-63d5-40f9-a627-fbdaf88caeab]
-description = "allergic to everything"
+description = "testing for peanuts allergy -> allergic to everything"
 
 [b948b0a1-cbf7-4b28-a244-73ff56687c80]
-description = "not allergic to anything"
+description = "testing for shellfish allergy -> not allergic to anything"
 
 [9ce9a6f3-53e9-4923-85e0-73019047c567]
-description = "allergic only to shellfish"
+description = "testing for shellfish allergy -> allergic only to shellfish"
 
 [b272fca5-57ba-4b00-bd0c-43a737ab2131]
-description = "allergic to shellfish and something else"
+description = "testing for shellfish allergy -> allergic to shellfish and something else"
 
 [21ef8e17-c227-494e-8e78-470a1c59c3d8]
-description = "allergic to something, but not shellfish"
+description = "testing for shellfish allergy -> allergic to something, but not shellfish"
 
 [cc789c19-2b5e-4c67-b146-625dc8cfa34e]
-description = "allergic to everything"
+description = "testing for shellfish allergy -> allergic to everything"
 
 [651bde0a-2a74-46c4-ab55-02a0906ca2f5]
-description = "not allergic to anything"
+description = "testing for strawberries allergy -> not allergic to anything"
 
 [b649a750-9703-4f5f-b7f7-91da2c160ece]
-description = "allergic only to strawberries"
+description = "testing for strawberries allergy -> allergic only to strawberries"
 
 [50f5f8f3-3bac-47e6-8dba-2d94470a4bc6]
-description = "allergic to strawberries and something else"
+description = "testing for strawberries allergy -> allergic to strawberries and something else"
 
 [23dd6952-88c9-48d7-a7d5-5d0343deb18d]
-description = "allergic to something, but not strawberries"
+description = "testing for strawberries allergy -> allergic to something, but not strawberries"
 
 [74afaae2-13b6-43a2-837a-286cd42e7d7e]
-description = "allergic to everything"
+description = "testing for strawberries allergy -> allergic to everything"
 
 [c49a91ef-6252-415e-907e-a9d26ef61723]
-description = "not allergic to anything"
+description = "testing for tomatoes allergy -> not allergic to anything"
 
 [b69c5131-b7d0-41ad-a32c-e1b2cc632df8]
-description = "allergic only to tomatoes"
+description = "testing for tomatoes allergy -> allergic only to tomatoes"
 
 [1ca50eb1-f042-4ccf-9050-341521b929ec]
-description = "allergic to tomatoes and something else"
+description = "testing for tomatoes allergy -> allergic to tomatoes and something else"
 
 [e9846baa-456b-4eff-8025-034b9f77bd8e]
-description = "allergic to something, but not tomatoes"
+description = "testing for tomatoes allergy -> allergic to something, but not tomatoes"
 
 [b2414f01-f3ad-4965-8391-e65f54dad35f]
-description = "allergic to everything"
+description = "testing for tomatoes allergy -> allergic to everything"
 
 [978467ab-bda4-49f7-b004-1d011ead947c]
-description = "not allergic to anything"
+description = "testing for chocolate allergy -> not allergic to anything"
 
 [59cf4e49-06ea-4139-a2c1-d7aad28f8cbc]
-description = "allergic only to chocolate"
+description = "testing for chocolate allergy -> allergic only to chocolate"
 
 [b0a7c07b-2db7-4f73-a180-565e07040ef1]
-description = "allergic to chocolate and something else"
+description = "testing for chocolate allergy -> allergic to chocolate and something else"
 
 [f5506893-f1ae-482a-b516-7532ba5ca9d2]
-description = "allergic to something, but not chocolate"
+description = "testing for chocolate allergy -> allergic to something, but not chocolate"
 
 [02debb3d-d7e2-4376-a26b-3c974b6595c6]
-description = "allergic to everything"
+description = "testing for chocolate allergy -> allergic to everything"
 
 [17f4a42b-c91e-41b8-8a76-4797886c2d96]
-description = "not allergic to anything"
+description = "testing for pollen allergy -> not allergic to anything"
 
 [7696eba7-1837-4488-882a-14b7b4e3e399]
-description = "allergic only to pollen"
+description = "testing for pollen allergy -> allergic only to pollen"
 
 [9a49aec5-fa1f-405d-889e-4dfc420db2b6]
-description = "allergic to pollen and something else"
+description = "testing for pollen allergy -> allergic to pollen and something else"
 
 [3cb8e79f-d108-4712-b620-aa146b1954a9]
-description = "allergic to something, but not pollen"
+description = "testing for pollen allergy -> allergic to something, but not pollen"
 
 [1dc3fe57-7c68-4043-9d51-5457128744b2]
-description = "allergic to everything"
+description = "testing for pollen allergy -> allergic to everything"
 
 [d3f523d6-3d50-419b-a222-d4dfd62ce314]
-description = "not allergic to anything"
+description = "testing for cats allergy -> not allergic to anything"
 
 [eba541c3-c886-42d3-baef-c048cb7fcd8f]
-description = "allergic only to cats"
+description = "testing for cats allergy -> allergic only to cats"
 
 [ba718376-26e0-40b7-bbbe-060287637ea5]
-description = "allergic to cats and something else"
+description = "testing for cats allergy -> allergic to cats and something else"
 
 [3c6dbf4a-5277-436f-8b88-15a206f2d6c4]
-description = "allergic to something, but not cats"
+description = "testing for cats allergy -> allergic to something, but not cats"
 
 [1faabb05-2b98-4995-9046-d83e4a48a7c1]
-description = "allergic to everything"
+description = "testing for cats allergy -> allergic to everything"
 
 [f9c1b8e7-7dc5-4887-aa93-cebdcc29dd8f]
-description = "no allergies"
+description = "list when: -> no allergies"
 
 [9e1a4364-09a6-4d94-990f-541a94a4c1e8]
-description = "just eggs"
+description = "list when: -> just eggs"
 
 [8851c973-805e-4283-9e01-d0c0da0e4695]
-description = "just peanuts"
+description = "list when: -> just peanuts"
 
 [2c8943cb-005e-435f-ae11-3e8fb558ea98]
-description = "just strawberries"
+description = "list when: -> just strawberries"
 
 [6fa95d26-044c-48a9-8a7b-9ee46ec32c5c]
-description = "eggs and peanuts"
+description = "list when: -> eggs and peanuts"
 
 [19890e22-f63f-4c5c-a9fb-fb6eacddfe8e]
-description = "more than eggs but not peanuts"
+description = "list when: -> more than eggs but not peanuts"
 
 [4b68f470-067c-44e4-889f-c9fe28917d2f]
-description = "lots of stuff"
+description = "list when: -> lots of stuff"
 
 [0881b7c5-9efa-4530-91bd-68370d054bc7]
-description = "everything"
+description = "list when: -> everything"
 
 [12ce86de-b347-42a0-ab7c-2e0570f0c65b]
-description = "no allergen score parts"
+description = "list when: -> no allergen score parts"
+
+[93c2df3e-4f55-4fed-8116-7513092819cd]
+description = "list when: -> no allergen score parts without highest valid score"
diff --git a/exercises/practice/allergies/test/allergies_test.clj b/exercises/practice/allergies/test/allergies_test.clj
index cda4aae..7d1acd8 100644
--- a/exercises/practice/allergies/test/allergies_test.clj
+++ b/exercises/practice/allergies/test/allergies_test.clj
@@ -1,49 +1,124 @@
 (ns allergies-test
-  (:require [clojure.test :refer [deftest is]]
+  (:require [clojure.test :refer [deftest testing is]]
             allergies))
 
-(deftest no-allergies-at-all
-  (is (= [] (allergies/allergies 0))))
+(deftest testing-for-eggs-allergy
+  (testing "Testing for eggs allergy -> not allergic to anything"
+    (is (false? (allergies/allergic-to? 0 :eggs))))
+  (testing "Testing for eggs allergy -> allergic only to eggs"
+    (is (true? (allergies/allergic-to? 1 :eggs))))
+  (testing "Testing for eggs allergy -> allergic to eggs and something else"
+    (is (true? (allergies/allergic-to? 3 :eggs))))
+  (testing "Testing for eggs allergy -> allergic to something, but not eggs"
+    (is (false? (allergies/allergic-to? 2 :eggs))))
+  (testing "Testing for eggs allergy -> allergic to everything"
+    (is (true? (allergies/allergic-to? 255 :eggs)))))
 
-(deftest allergic-to-just-eggs
-  (is (= [:eggs] (allergies/allergies 1))))
+(deftest testing-for-peanuts-allergy
+  (testing "Testing for peanuts allergy -> not allergic to anything"
+    (is (false? (allergies/allergic-to? 0 :peanuts))))
+  (testing "Testing for peanuts allergy -> allergic only to peanuts"
+    (is (true? (allergies/allergic-to? 2 :peanuts))))
+  (testing "Testing for peanuts allergy -> allergic to peanuts and something else"
+    (is (true? (allergies/allergic-to? 7 :peanuts))))
+  (testing "Testing for peanuts allergy -> allergic to something, but not peanuts"
+    (is (false? (allergies/allergic-to? 5 :peanuts))))
+  (testing "Testing for peanuts allergy -> allergic to everything"
+    (is (true? (allergies/allergic-to? 255 :peanuts)))))
 
-(deftest allergic-to-just-peanuts
-  (is (= [:peanuts] (allergies/allergies 2))))
+(deftest testing-for-shellfish-allergy
+  (testing "Testing for shellfish allergy -> not allergic to anything"
+    (is (false? (allergies/allergic-to? 0 :shellfish))))
+  (testing "Testing for shellfish allergy -> allergic only to shellfish"
+    (is (true? (allergies/allergic-to? 4 :shellfish))))
+  (testing "Testing for shellfish allergy -> allergic to shellfish and something else"
+    (is (true? (allergies/allergic-to? 14 :shellfish))))
+  (testing "Testing for shellfish allergy -> allergic to something, but not shellfish"
+    (is (false? (allergies/allergic-to? 10 :shellfish))))
+  (testing "Testing for shellfish allergy -> allergic to everything"
+    (is (true? (allergies/allergic-to? 255 :shellfish)))))
 
-(deftest allergic-to-just-strawberries
-  (is (= [:strawberries] (allergies/allergies 8))))
+(deftest testing-for-strawberries-allergy
+  (testing "Testing for strawberries allergy -> not allergic to anything"
+    (is (false? (allergies/allergic-to? 0 :strawberries))))
+  (testing "Testing for strawberries allergy -> allergic only to strawberries"
+    (is (true? (allergies/allergic-to? 8 :strawberries))))
+  (testing "Testing for strawberries allergy -> allergic to strawberries and something else"
+    (is (true? (allergies/allergic-to? 28 :strawberries))))
+  (testing "Testing for strawberries allergy -> allergic to something, but not strawberries"
+    (is (false? (allergies/allergic-to? 20 :strawberries))))
+  (testing "Testing for strawberries allergy -> allergic to everything"
+    (is (true? (allergies/allergic-to? 255 :strawberries)))))
 
-(deftest allergic-to-eggs-and-peanuts
-  (is (= [:eggs :peanuts] (allergies/allergies 3))))
+(deftest testing-for-tomatoes-allergy
+  (testing "Testing for tomatoes allergy -> not allergic to anything"
+    (is (false? (allergies/allergic-to? 0 :tomatoes))))
+  (testing "Testing for tomatoes allergy -> allergic only to tomatoes"
+    (is (true? (allergies/allergic-to? 16 :tomatoes))))
+  (testing "Testing for tomatoes allergy -> allergic to tomatoes and something else"
+    (is (true? (allergies/allergic-to? 56 :tomatoes))))
+  (testing "Testing for tomatoes allergy -> allergic to something, but not tomatoes"
+    (is (false? (allergies/allergic-to? 40 :tomatoes))))
+  (testing "Testing for tomatoes allergy -> allergic to everything"
+    (is (true? (allergies/allergic-to? 255 :tomatoes)))))
 
-(deftest allergic-to-more-than-eggs-but-not-peanuts
-  (is (= [:eggs :shellfish] (allergies/allergies 5))))
+(deftest testing-for-chocolate-allergy
+  (testing "Testing for chocolate allergy -> not allergic to anything"
+    (is (false? (allergies/allergic-to? 0 :chocolate))))
+  (testing "Testing for chocolate allergy -> allergic only to chocolate"
+    (is (true? (allergies/allergic-to? 32 :chocolate))))
+  (testing "Testing for chocolate allergy -> allergic to chocolate and something else"
+    (is (true? (allergies/allergic-to? 112 :chocolate))))
+  (testing "Testing for chocolate allergy -> allergic to something, but not chocolate"
+    (is (false? (allergies/allergic-to? 80 :chocolate))))
+  (testing "Testing for chocolate allergy -> allergic to everything"
+    (is (true? (allergies/allergic-to? 255 :chocolate)))))
 
-(deftest allergic-to-lots-of-stuff
-  (is (= [:strawberries :tomatoes :chocolate :pollen :cats]
-         (allergies/allergies 248))))
+(deftest testing-for-pollen-allergy
+  (testing "Testing for pollen allergy -> not allergic to anything"
+    (is (false? (allergies/allergic-to? 0 :pollen))))
+  (testing "Testing for pollen allergy -> allergic only to pollen"
+    (is (true? (allergies/allergic-to? 64 :pollen))))
+  (testing "Testing for pollen allergy -> allergic to pollen and something else"
+    (is (true? (allergies/allergic-to? 224 :pollen))))
+  (testing "Testing for pollen allergy -> allergic to something, but not pollen"
+    (is (false? (allergies/allergic-to? 160 :pollen))))
+  (testing "Testing for pollen allergy -> allergic to everything"
+    (is (true? (allergies/allergic-to? 255 :pollen)))))
 
-(deftest allergic-to-everything
-  (is (= [:eggs :peanuts :shellfish :strawberries
-          :tomatoes :chocolate :pollen :cats]
-         (allergies/allergies 255))))
+(deftest testing-for-cats-allergy
+  (testing "Testing for cats allergy -> not allergic to anything"
+    (is (false? (allergies/allergic-to? 0 :cats))))
+  (testing "Testing for cats allergy -> allergic only to cats"
+    (is (true? (allergies/allergic-to? 128 :cats))))
+  (testing "Testing for cats allergy -> allergic to cats and something else"
+    (is (true? (allergies/allergic-to? 192 :cats))))
+  (testing "Testing for cats allergy -> allergic to something, but not cats"
+    (is (false? (allergies/allergic-to? 64 :cats))))
+  (testing "Testing for cats allergy -> allergic to everything"
+    (is (true? (allergies/allergic-to? 255 :cats)))))
 
-(deftest no-allergies-means-not-allergic-peanuts
-  (is (not (allergies/allergic-to? 0 :peanuts))))
-
-(deftest no-allergies-means-not-allergic-cats
-  (is (not (allergies/allergic-to? 0 :cats))))
-
-(deftest no-allergies-means-not-allergic-strawberries
-  (is (not (allergies/allergic-to? 0 :strawberries))))
-
-(deftest is-allergic-to-eggs
-  (is (allergies/allergic-to? 1 :eggs)))
-
-(deftest allergic-to-eggs-in-addition-to-other-stuff
-  (is (allergies/allergic-to? 5 :eggs)))
-
-(deftest ignore-non-allergen-score-parts
-  (is (= [:eggs :shellfish :strawberries :tomatoes :chocolate :pollen :cats]
-         (allergies/allergies 509))))
+(deftest list-when
+  (testing "List when: no allergies"
+    (is (= [] (allergies/allergies 0))))
+  (testing "List when: allergic to just eggs"
+    (is (= [:eggs] (allergies/allergies 1))))
+  (testing "List when: allergic to just peanuts"
+    (is (= [:peanuts] (allergies/allergies 2))))
+  (testing "List when: allergic to just strawberries"
+    (is (= [:strawberries] (allergies/allergies 8))))
+  (testing "List when: allergic to eggs and peanuts"
+    (is (= [:eggs :peanuts] (allergies/allergies 3))))
+  (testing "List when: allergic to more than eggs but not peanuts"
+    (is (= [:eggs :shellfish] (allergies/allergies 5))))
+  (testing "List when: allergic to lots of stuff"
+    (is (= [:strawberries :tomatoes :chocolate :pollen :cats]
+           (allergies/allergies 248))))
+  (testing "List when: allergic to everything"
+    (is (= [:eggs :peanuts :shellfish :strawberries :tomatoes :chocolate :pollen :cats]
+           (allergies/allergies 255))))
+  (testing "List when: no allergen score parts"
+    (is (= [:eggs :shellfish :strawberries :tomatoes :chocolate :pollen :cats]
+           (allergies/allergies 509))))
+  (testing "List when: no allergen score parts without highest valid score"
+    (is (= [:eggs] (allergies/allergies 257)))))
diff --git a/exercises/practice/anagram/.meta/tests.toml b/exercises/practice/anagram/.meta/tests.toml
index 7fb93f7..4d90562 100644
--- a/exercises/practice/anagram/.meta/tests.toml
+++ b/exercises/practice/anagram/.meta/tests.toml
@@ -1,12 +1,24 @@
-# This is an auto-generated file. Regular comments will be removed when this
-# file is regenerated. Regenerating will not touch any manually added keys,
-# so comments can be added in a "comment" key.
+# 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.
 
 [dd40c4d2-3c8b-44e5-992a-f42b393ec373]
 description = "no matches"
 
 [b3cca662-f50a-489e-ae10-ab8290a09bdc]
 description = "detects two anagrams"
+include = false
+
+[03eb9bbe-8906-4ea0-84fa-ffe711b52c8b]
+description = "detects two anagrams"
+reimplements = "b3cca662-f50a-489e-ae10-ab8290a09bdc"
 
 [a27558ee-9ba0-4552-96b1-ecf665b06556]
 description = "does not detect anagram subsets"
@@ -34,12 +46,41 @@ description = "detects anagrams using case-insensitive possible matches"
 
 [7cc195ad-e3c7-44ee-9fd2-d3c344806a2c]
 description = "does not detect an anagram if the original word is repeated"
+include = false
+
+[630abb71-a94e-4715-8395-179ec1df9f91]
+description = "does not detect an anagram if the original word is repeated"
+reimplements = "7cc195ad-e3c7-44ee-9fd2-d3c344806a2c"
 
 [9878a1c9-d6ea-4235-ae51-3ea2befd6842]
 description = "anagrams must use all letters exactly once"
 
 [85757361-4535-45fd-ac0e-3810d40debc1]
 description = "words are not anagrams of themselves (case-insensitive)"
+include = false
+
+[68934ed0-010b-4ef9-857a-20c9012d1ebf]
+description = "words are not anagrams of themselves"
+reimplements = "85757361-4535-45fd-ac0e-3810d40debc1"
+
+[589384f3-4c8a-4e7d-9edc-51c3e5f0c90e]
+description = "words are not anagrams of themselves even if letter case is partially different"
+reimplements = "85757361-4535-45fd-ac0e-3810d40debc1"
+
+[ba53e423-7e02-41ee-9ae2-71f91e6d18e6]
+description = "words are not anagrams of themselves even if letter case is completely different"
+reimplements = "85757361-4535-45fd-ac0e-3810d40debc1"
 
 [a0705568-628c-4b55-9798-82e4acde51ca]
 description = "words other than themselves can be anagrams"
+include = false
+
+[33d3f67e-fbb9-49d3-a90e-0beb00861da7]
+description = "words other than themselves can be anagrams"
+reimplements = "a0705568-628c-4b55-9798-82e4acde51ca"
+
+[a6854f66-eec1-4afd-a137-62ef2870c051]
+description = "handles case of greek letters"
+
+[fd3509e5-e3ba-409d-ac3d-a9ac84d13296]
+description = "different characters may have the same bytes"
diff --git a/exercises/practice/anagram/test/anagram_test.clj b/exercises/practice/anagram/test/anagram_test.clj
index 17b5703..d8c1d53 100644
--- a/exercises/practice/anagram/test/anagram_test.clj
+++ b/exercises/practice/anagram/test/anagram_test.clj
@@ -1,38 +1,93 @@
 (ns anagram-test
-  (:require [clojure.test :refer [deftest is]]
+  (:require [clojure.test :refer [deftest testing is]]
             anagram))
 
 (deftest no-matches
-  (is (= []
-         (anagram/anagrams-for "diaper" ["hello" "world" "zombies" "pants"]))))
+  (testing "No matches"
+    (is (= []
+           (anagram/anagrams-for "diaper" ["hello" "world" "zombies" "pants"])))))
 
-(deftest detect-simple-anagram
-  (is (= ["tan"] (anagram/anagrams-for "ant" ["tan" "stand" "at"]))))
+(deftest detects-two-anagrams
+  (testing "Detects two anagrams"
+    (is (= ["lemons", "melons"]
+           (anagram/anagrams-for "solemn" ["lemons", "cherry", "melons"])))))
 
-(deftest does-not-confuse-different-duplicates
-  (is (= [] (anagram/anagrams-for "galea" ["eagle"]))))
+(deftest does-not-detect-anagram-subsets
+  (testing "Does not detect anagram subsets"
+    (is (= []
+           (anagram/anagrams-for "good" ["dog", "goody"])))))
 
-(deftest eliminate-anagram-subsets
-  (is (= [] (anagram/anagrams-for "good" ["dog" "goody"]))))
+(deftest detects-anagram
+  (testing "Detects anagram"
+    (is (= ["inlets"]
+           (anagram/anagrams-for "listen" ["enlists", "google", "inlets", "banana"])))))
 
-(deftest detect-anagram
-  (is (= ["inlets"]
-         (let [coll ["enlists" "google" "inlets" "banana"]]
-           (anagram/anagrams-for "listen" coll)))))
+(deftest detects-three-anagrams
+  (testing "Detects three anagrams"
+    (is (= ["gallery", "regally", "largely"]
+           (anagram/anagrams-for "allergy" ["gallery", "ballerina", "regally", "clergy", "largely", "leading"])))))
 
-(deftest multiple-anagrams
-  (is (= ["gallery" "regally" "largely"]
-         (let [coll ["gallery" "ballerina" "regally"
-                     "clergy"  "largely"   "leading"]]
-           (anagram/anagrams-for "allergy" coll)))))
+(deftest detects-multiple-anagrams-with-different-case
+  (testing "Detects multiple anagrams with different case"
+    (is (= ["Eons", "ONES"]
+           (anagram/anagrams-for "nose" ["Eons", "ONES"])))))
 
-(deftest case-insensitive-anagrams
-  (is (= ["Carthorse"]
-         (let [coll ["cashregister" "Carthorse" "radishes"]]
-           (anagram/anagrams-for "Orchestra" coll)))))
+(deftest does-not-detect-non-anagrams-with-identical-checksum
+  (testing "Does not detect non-anagrams with identical checksum"
+    (is (= []
+           (anagram/anagrams-for "mass" ["last"])))))
 
-(deftest word-is-not-own-anagram
-  (is (= [] (anagram/anagrams-for "banana" ["banana"]))))
+(deftest detects-anagrams-case-insensitively
+  (testing "Detects anagrams case-insensitively"
+    (is (= ["Carthorse"]
+           (anagram/anagrams-for "Orchestra" ["cashregister", "Carthorse", "radishes"])))))
 
-(deftest capital-word-is-not-own-anagram
-  (is (= [] (anagram/anagrams-for "BANANA" ["banana"]))))
+(deftest detects-anagrams-using-case-insensitive-subject
+  (testing "Detects anagrams using case-insensitive subject"
+    (is (= ["carthorse"]
+           (anagram/anagrams-for "Orchestra" ["cashregister", "carthorse", "radishes"])))))
+
+(deftest detects-anagrams-using-case-insensitive-possible-matches
+  (testing "Detects anagrams using case-insensitive possible matches"
+    (is (= ["Carthorse"]
+           (anagram/anagrams-for "orchestra" ["cashregister", "Carthorse", "radishes"])))))
+
+(deftest does-not-detect-anagram-if-original-word-is-repeated
+  (testing "Does not detect an anagram if the original word is repeated"
+    (is (= []
+           (anagram/anagrams-for "go" ["goGoGO"])))))
+
+(deftest anagrams-must-use-all-letters-exactly-once
+  (testing "Anagrams must use all letters exactly once"
+    (is (= []
+           (anagram/anagrams-for "tapper" ["patter"])))))
+
+(deftest words-are-not-anagrams-of-themselves
+  (testing "Words are not anagrams of themselves"
+    (is (= []
+           (anagram/anagrams-for "BANANA" ["BANANA"])))))
+
+(deftest words-are-not-anagrams-of-themselves-even-if-letter-case-is-partially-different
+  (testing "Words are not anagrams of themselves even if letter case is partially different"
+    (is (= []
+           (anagram/anagrams-for "BANANA" ["Banana"])))))
+
+(deftest words-are-not-anagrams-of-themselves-even-if-letter-case-is-completely-different
+  (testing "Words are not anagrams of themselves even if letter case is completely different"
+    (is (= []
+           (anagram/anagrams-for "BANANA" ["banana"])))))
+
+(deftest words-other-than-themselves-can-be-anagrams
+  (testing "Words other than themselves can be anagrams"
+    (is (= ["Silent"]
+           (anagram/anagrams-for "LISTEN" ["LISTEN", "Silent"])))))
+
+(deftest handles-greek-letters
+  (testing "Handles case of greek letters"
+    (is (= ["ΒΓΑ", "γβα"]
+           (anagram/anagrams-for "ΑΒΓ" ["ΒΓΑ", "ΒΓΔ", "γβα", "αβγ"])))))
+
+(deftest different-characters-may-have-same-bytes
+  (testing "Different characters may have the same bytes"
+    (is (= []
+           (anagram/anagrams-for "a⬂" ["€a"])))))
diff --git a/exercises/practice/binary-search/test/binary_search_test.clj b/exercises/practice/binary-search/test/binary_search_test.clj
index 00efed5..60f8464 100644
--- a/exercises/practice/binary-search/test/binary_search_test.clj
+++ b/exercises/practice/binary-search/test/binary_search_test.clj
@@ -1,40 +1,52 @@
 (ns binary-search-test
-  (:require [clojure.test :refer [deftest is]]
+  (:require [clojure.test :refer [deftest is testing]]
             binary-search))
 
-(def short-vector [1, 3, 4, 6, 8, 9, 11])
-
-(def large-vector [1, 3, 5, 8, 13, 21, 34, 55, 89])
-
-(def even-length-vector [1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377])
-
-(deftest it-finds-position-of-middle-item
-  (is (= 3 (binary-search/middle short-vector))))
-
-(deftest searches-a-singleton
-  (is (= 0 (binary-search/search-for 4 [4]))))
-
-(deftest it-finds-position-of-search-data
-  (is (= 5 (binary-search/search-for 9 short-vector))))
-
-(deftest it-finds-position-in-a-larger-list
-  (is (= 1 (binary-search/search-for 3 large-vector))))
-
-(deftest it-finds-position-in-a-larger-list-again
-  (is (= 7 (binary-search/search-for 55 large-vector))))
-
-(deftest it-finds-correct-position-in-a-list-with-an-even-number-of-elements
-  (is (= 5 (binary-search/search-for 21 even-length-vector))))
-
-(deftest it-finds-correct-position-in-a-list-with-an-even-number-of-elements-again
-  (is (= 6 (binary-search/search-for 34 even-length-vector))))
-
-(deftest it-works-on-lists
-  (is (= 7 (binary-search/search-for 7 (range 10)))))
-
-(deftest it-works-on-lists-again
-  (is (= 4 (binary-search/search-for 3 '(-3 -2 0 1 3 4)))))
-
-(deftest throws-exception-when-element-not-found
-  (is (thrown-with-msg? Throwable #"not found"
-                        (binary-search/search-for 20 short-vector))))
+(deftest finds-value-in-array-with-one-element
+  (testing "Finds a value in an array with one element"
+           (is (= 0 (binary-search/search-for 6 [6])))))
+
+(deftest finds-value-in-middle-of-array
+  (testing "Finds a value in the middle of an array"
+    (is (= 3 (binary-search/search-for 6 [1, 3, 4, 6, 8, 9, 11])))))
+
+(deftest finds-value-at-beginning-of-array
+  (testing "Finds a value at the beginning of an array"
+    (is (= 0 (binary-search/search-for 1 [1, 3, 4, 6, 8, 9, 11])))))
+
+(deftest finds-value-at-end-of-array
+  (testing "Finds a value at the end of an array"
+    (is (= 6 (binary-search/search-for 11 [1, 3, 4, 6, 8, 9, 11])))))
+
+(deftest finds-value-in-array-of-odd-length
+  (testing "Finds a value in an array of odd length"
+    (is (= 9 (binary-search/search-for 144 [1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 634])))))
+
+(deftest finds-value-in-array-of-even-length
+  (testing "Finds a value in an array of even length"
+    (is (= 5 (binary-search/search-for 21 [1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377])))))
+
+(deftest identifies-that-value-is-not-in-array
+  (testing "Identifies that a value is not included in the array"
+    (is (thrown-with-msg? Throwable #"not found"
+                          (binary-search/search-for 7 [1, 3, 4, 6, 8, 9, 11])))))
+
+(deftest value-smaller-than-arrays-smallest-value-not-found
+  (testing "A value smaller than the array's smallest value is not found"
+    (is (thrown-with-msg? Throwable #"not found"
+                          (binary-search/search-for 0 [1, 3, 4, 6, 8, 9, 11])))))
+
+(deftest value-larger-than-arrays-largest-value-not-found
+  (testing "A value larger than the array's largest value is not found"
+    (is (thrown-with-msg? Throwable #"not found"
+                          (binary-search/search-for 13 [1, 3, 4, 6, 8, 9, 11])))))
+
+(deftest nothing-found-in-empty-array
+  (testing "Nothing is found in an empty array"
+    (is (thrown-with-msg? Throwable #"not found"
+                          (binary-search/search-for 1 [])))))
+
+(deftest nothing-found-when-left-and-right-bounds-cross
+  (testing "Nothing is found when the left and right bounds cross"
+    (is (thrown-with-msg? Throwable #"not found"
+                          (binary-search/search-for 0 [1, 2])))))
diff --git a/exercises/practice/collatz-conjecture/.meta/tests.toml b/exercises/practice/collatz-conjecture/.meta/tests.toml
index 04187f6..cc34e16 100644
--- a/exercises/practice/collatz-conjecture/.meta/tests.toml
+++ b/exercises/practice/collatz-conjecture/.meta/tests.toml
@@ -1,6 +1,13 @@
-# This is an auto-generated file. Regular comments will be removed when this
-# file is regenerated. Regenerating will not touch any manually added keys,
-# so comments can be added in a "comment" key.
+# 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.
 
 [540a3d51-e7a6-47a5-92a3-4ad1838f0bfd]
 description = "zero steps for one"
@@ -16,6 +23,16 @@ description = "large number of even and odd steps"
 
 [7d4750e6-def9-4b86-aec7-9f7eb44f95a3]
 description = "zero is an error"
+include = false
+
+[2187673d-77d6-4543-975e-66df6c50e2da]
+description = "zero is an error"
+reimplements = "7d4750e6-def9-4b86-aec7-9f7eb44f95a3"
 
 [c6c795bf-a288-45e9-86a1-841359ad426d]
 description = "negative value is an error"
+include = false
+
+[ec11f479-56bc-47fd-a434-bcd7a31a7a2e]
+description = "negative value is an error"
+reimplements = "c6c795bf-a288-45e9-86a1-841359ad426d"
diff --git a/exercises/practice/luhn/.meta/tests.toml b/exercises/practice/luhn/.meta/tests.toml
index 9bb8224..c0be0c4 100644
--- a/exercises/practice/luhn/.meta/tests.toml
+++ b/exercises/practice/luhn/.meta/tests.toml
@@ -1,6 +1,13 @@
-# This is an auto-generated file. Regular comments will be removed when this
-# file is regenerated. Regenerating will not touch any manually added keys,
-# so comments can be added in a "comment" key.
+# 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.
 
 [792a7082-feb7-48c7-b88b-bbfec160865e]
 description = "single digit strings can not be valid"
@@ -26,6 +33,9 @@ description = "invalid credit card"
 [20e67fad-2121-43ed-99a8-14b5b856adb9]
 description = "invalid long number with an even remainder"
 
+[7e7c9fc1-d994-457c-811e-d390d52fba5e]
+description = "invalid long number with a remainder divisible by 5"
+
 [ad2a0c5f-84ed-4e5b-95da-6011d6f4f0aa]
 description = "valid number with an even number of digits"
 
@@ -50,8 +60,17 @@ description = "more than a single zero is valid"
 [ab56fa80-5de8-4735-8a4a-14dae588663e]
 description = "input digit 9 is correctly converted to output digit 9"
 
+[b9887ee8-8337-46c5-bc45-3bcab51bc36f]
+description = "very long input is valid"
+
+[8a7c0e24-85ea-4154-9cf1-c2db90eabc08]
+description = "valid luhn with an odd number of digits and non zero first digit"
+
 [39a06a5a-5bad-4e0f-b215-b042d46209b1]
 description = "using ascii value for non-doubled non-digit isn't allowed"
 
 [f94cf191-a62f-4868-bc72-7253114aa157]
 description = "using ascii value for doubled non-digit isn't allowed"
+
+[8b72ad26-c8be-49a2-b99c-bcc3bf631b33]
+description = "non-numeric, non-space char in the middle with a sum that's divisible by 10 isn't allowed"
diff --git a/exercises/practice/luhn/test/luhn_test.clj b/exercises/practice/luhn/test/luhn_test.clj
index d706786..892c03f 100644
--- a/exercises/practice/luhn/test/luhn_test.clj
+++ b/exercises/practice/luhn/test/luhn_test.clj
@@ -2,41 +2,90 @@
   (:require [clojure.test :refer [deftest is testing]]
             luhn))
 
-(deftest single-digit-strings
-    (is (false? (luhn/valid? "1"))))
+(deftest single-digit-strings-are-invalid
+  (testing "Single digit strings can not be valid"
+    (is (false? (luhn/valid? "1")))))
 
-(deftest single-zero
-    (is (false? (luhn/valid? "0"))))
+(deftest single-zero-is-invalid
+  (testing "A single zero is invalid"
+    (is (false? (luhn/valid? "0")))))
 
-(deftest sin
-    (is (true? (luhn/valid? " 5 9 "))))
+(deftest simple-valid-SIN-that-remains-valid-if-reversed
+  (testing "A simple valid SIN that remains valid if reversed"
+    (is (true? (luhn/valid? "059")))))
 
-(deftest Canadian-SIN
-    (is (true? (luhn/valid? "046 454 286"))))
+(deftest simple-valid-SIN-that-becomes-invalid-if-reversed
+  (testing "A simple valid SIN that becomes invalid if reversed"
+    (is (true? (luhn/valid? "59")))))
 
-(deftest invalid-Canadian-SIN
-    (is (false? (luhn/valid? "046 454 287"))))
+(deftest valid-canadian-SIN
+  (testing "A valid Canadian SIN"
+    (is (true? (luhn/valid? "055 444 285")))))
+
+(deftest invalid-canadian-SIN
+  (testing "Invalid Canadian SIN"
+    (is (false? (luhn/valid? "055 444 286")))))
 
 (deftest invalid-credit-card
-    (is (false? (luhn/valid? "8273 1232 7352 0569"))))
+  (testing "Invalid credit card"
+    (is (false? (luhn/valid? "8273 1232 7352 0569")))))
+
+(deftest invalid-long-number-with-an-even-remainder
+  (testing "Invalid long number with an even remainder"
+    (is (false? (luhn/valid? "1 2345 6789 1234 5678 9012")))))
+
+(deftest invalid-long-number-with-a-remainder-divisible-by-5
+  (testing "Invalid long number with a remainder divisible by 5"
+    (is (false? (luhn/valid? "1 2345 6789 1234 5678 9013")))))
+
+(deftest valid-number-with-an-even-number-of-digits
+  (testing "Valid number with an even number of digits"
+    (is (true? (luhn/valid? "095 245 88")))))
+
+(deftest valid-number-with-an-odd-number-of-spaces
+  (testing "Valid number with an odd number of spaces"
+    (is (true? (luhn/valid? "234 567 891 234")))))
+
+(deftest valid-strings-with-a-non-digit-added-at-the-end-become-invalid
+  (testing "Valid strings with a non-digit added at the end become invalid"
+    (is (false? (luhn/valid? "059a")))))
+
+(deftest valid-strings-with-punctuation-included-become-invalid
+  (testing "Valid strings with punctuation included become invalid"
+    (is (false? (luhn/valid? "055-444-285")))))
+
+(deftest valid-strings-with-symbols-included-become-invalid
+  (testing "Valid strings with symbols included become invalid"
+    (is (false? (luhn/valid? "055# 444$ 285")))))
+
+(deftest single-zero-with-space-is-invalid
+  (testing "Single zero with space is invalid"
+    (is (false? (luhn/valid? " 0")))))
 
-(deftest non-digit-added
-    (is (false? (luhn/valid? "046a 454 286"))))
+(deftest more-than-a-single-zero-is-valid
+  (testing "More than a single zero is valid"
+    (is (true? (luhn/valid? "0000 0")))))
 
-(deftest punctuation
-    (is (false? (luhn/valid? "055-444-285"))))
+(deftest input-digit-9-is-correctly-converted-to-output-digit-9
+  (testing "Input digit 9 is correctly converted to output digit 9"
+    (is (true? (luhn/valid? "091")))))
 
-(deftest symbols
-    (is (false? (luhn/valid? "055£ 444$ 285"))))
+(deftest very-long-input-is-valid
+  (testing "Very long input is valid"
+    (is (true? (luhn/valid? "9999999999 9999999999 9999999999 9999999999")))))
 
-(deftest single-zero-with-space
-    (is (false? (luhn/valid? " 0"))))
+(deftest valid-luhn-with-an-odd-number-of-digits-and-non-zero-first-digit
+  (testing "Valid luhn with an odd number of digits and non zero first digit"
+    (is (true? (luhn/valid? "109")))))
 
-(deftest lots-of-zeros
-    (is (true? (luhn/valid? " 00000"))))
+(deftest using-ascii-value-for-non-doubled-non-digit-is-not-allowed
+  (testing "Using ascii value for non-doubled non-digit isn't allowed"
+    (is (false? (luhn/valid? "055b 444 285")))))
 
-(deftest another-valid-sin
-    (is (true? (luhn/valid? "055 444 285"))))
+(deftest using-ascii-value-for-doubled-non-digit-is-not-allowed
+  (testing "Using ascii value for doubled non-digit isn't allowed"
+    (is (false? (luhn/valid? ":9")))))
 
-(deftest nine-doubled
-    (is (true? (luhn/valid? "091"))))
+(deftest non-numeric-non-space-char-in-middle-with-sum-that-is-divisible-by-10-is-not-allowed
+  (testing "Non-numeric, non-space char in the middle with a sum that's divisible by 10 isn't allowed"
+    (is (false? (luhn/valid? "59%59")))))
diff --git a/exercises/practice/matching-brackets/.meta/tests.toml b/exercises/practice/matching-brackets/.meta/tests.toml
index cc9e471..35a98a0 100644
--- a/exercises/practice/matching-brackets/.meta/tests.toml
+++ b/exercises/practice/matching-brackets/.meta/tests.toml
@@ -1,6 +1,13 @@
-# This is an auto-generated file. Regular comments will be removed when this
-# file is regenerated. Regenerating will not touch any manually added keys,
-# so comments can be added in a "comment" key.
+# 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.
 
 [81ec11da-38dd-442a-bcf9-3de7754609a5]
 description = "paired square brackets"
@@ -41,12 +48,21 @@ description = "unpaired and nested brackets"
 [a0205e34-c2ac-49e6-a88a-899508d7d68e]
 description = "paired and wrong nested brackets"
 
+[1d5c093f-fc84-41fb-8c2a-e052f9581602]
+description = "paired and wrong nested brackets but innermost are correct"
+
 [ef47c21b-bcfd-4998-844c-7ad5daad90a8]
 description = "paired and incomplete brackets"
 
 [a4675a40-a8be-4fc2-bc47-2a282ce6edbe]
 description = "too many closing brackets"
 
+[a345a753-d889-4b7e-99ae-34ac85910d1a]
+description = "early unexpected brackets"
+
+[21f81d61-1608-465a-b850-baa44c5def83]
+description = "early mismatched brackets"
+
 [99255f93-261b-4435-a352-02bdecc9bdf2]
 description = "math expression"
 
diff --git a/exercises/practice/matching-brackets/test/matching_brackets_test.clj b/exercises/practice/matching-brackets/test/matching_brackets_test.clj
index d84e5c7..21c7e86 100644
--- a/exercises/practice/matching-brackets/test/matching_brackets_test.clj
+++ b/exercises/practice/matching-brackets/test/matching_brackets_test.clj
@@ -1,50 +1,83 @@
 (ns matching-brackets-test
-  (:require [clojure.test :refer [deftest is]]
+  (:require [clojure.test :refer [deftest testing is]]
             matching-brackets))
 
-(deftest paired-square
-  (is (matching-brackets/valid? "[]") "paired square brackets"))
+(deftest paired-square-brackets
+  (testing "paired square brackets"
+    (is (matching-brackets/valid? "[]"))))
 
 (deftest empty-string
-  (is (matching-brackets/valid? "") "empty string"))
+  (testing "Empty string"
+    (is (matching-brackets/valid? ""))))
 
 (deftest unpaired-brackets
-  (is (false? (matching-brackets/valid? "[[")) "unpaired brackets"))
+  (testing "Unpaired brackets"
+    (is (false? (matching-brackets/valid? "[[")))))
 
 (deftest wrong-ordered-brackets
-  (is (false? (matching-brackets/valid? "}{")) "wrong ordered brackets"))
+  (testing "Wrong ordered brackets"
+    (is (false? (matching-brackets/valid? "}{")))))
+
+(deftest wrong-closing-bracket
+  (testing "Wrong closing bracket"
+    (is (false? (matching-brackets/valid? "{]")))))
 
 (deftest paired-with-whitespace
-  (is (matching-brackets/valid? "{ }") "paired with whitespace"))
+  (testing "Paired with whitespace"
+    (is (matching-brackets/valid? "{ }"))))
+
+(deftest partially-paired-brackets
+  (testing "Partially paired brackets"
+    (is (false? (matching-brackets/valid? "{[])")))))
 
 (deftest simple-nested-brackets
-  (is (matching-brackets/valid? "{[]}")
-      "simple nested brackets"))
+  (testing "Simple nested brackets"
+    (is (matching-brackets/valid? "{[]}"))))
 
 (deftest several-paired-brackets
-  (is (matching-brackets/valid? "{}[]")
-      "several paired brackets"))
+  (testing "Several paired brackets"
+    (is (matching-brackets/valid? "{}[]"))))
 
 (deftest paired-and-nested-brackets
-  (is (matching-brackets/valid? "([{}({}[])])")
-      "paired and nested brackets"))
+  (testing "Paired and nested brackets"
+    (is (matching-brackets/valid? "([{}({}[])])"))))
 
 (deftest unopened-closing-brackets
-  (is (false? (matching-brackets/valid? "{[)][]}"))
-      "unopened closing brackets"))
+  (testing "Unopened closing brackets"
+    (is (false? (matching-brackets/valid? "{[)][]}")))))
  
 (deftest unpaired-and-nested-brackets
-  (is (false? (matching-brackets/valid? "([{])"))
-      "unpaired and nested brackets"))
+  (testing "Unpaired and nested brackets"
+    (is (false? (matching-brackets/valid? "([{])")))))
 
 (deftest paired-and-wrong-nested-brackets
-  (is (false? (matching-brackets/valid? "[({]})"))
-      "paired and wrong nested brackets"))
+  (testing "Paired and wrong nested brackets"
+    (is (false? (matching-brackets/valid? "[({]})")))))
+
+(deftest paired-and-wrong-nested-brackets-but-innermost-are-correct
+  (testing "Paired and wrong nested brackets but innermost are correct"
+    (is (false? (matching-brackets/valid? "[({}])")))))
+
+(deftest paired-and-incomplete-brackets
+  (testing "Paired and incomplete brackets"
+    (is (false? (matching-brackets/valid? "{}[")))))
+
+(deftest too-many-closing-brackets
+  (testing "Too many closing brackets"
+    (is (false? (matching-brackets/valid? "[]]")))))
+
+(deftest early-unexpected-brackets
+  (testing "Early unexpected brackets"
+    (is (false? (matching-brackets/valid? ")()")))))
+
+(deftest early-mismatched-brackets
+  (testing "Early mismatched brackets"
+    (is (false? (matching-brackets/valid? "{)()")))))
 
 (deftest math-expression
-  (is (matching-brackets/valid? "(((185 + 223.85) * 15) - 543)/2")
-      "math expression"))
+  (testing "Math expression"
+    (is (matching-brackets/valid? "(((185 + 223.85) * 15) - 543)/2"))))
 
 (deftest complex-latex-expression
-  (is (matching-brackets/valid? "\\\\left(\\\\begin{array}{cc} \\\\frac{1}{3} & x\\\\\\\\ \\\\mathrm{e}^{x} &... x^2 \\\\end{array}\\\\right)")
-      "complex latex expression"))
+  (testing "Complex latex expression"
+    (is (matching-brackets/valid? "\\\\left(\\\\begin{array}{cc} \\\\frac{1}{3} & x\\\\\\\\ \\\\mathrm{e}^{x} &... x^2 \\\\end{array}\\\\right)"))))
diff --git a/exercises/practice/protein-translation/.meta/tests.toml b/exercises/practice/protein-translation/.meta/tests.toml
index 02a54c3..10ee1ee 100644
--- a/exercises/practice/protein-translation/.meta/tests.toml
+++ b/exercises/practice/protein-translation/.meta/tests.toml
@@ -1,6 +1,16 @@
-# This is an auto-generated file. Regular comments will be removed when this
-# file is regenerated. Regenerating will not touch any manually added keys,
-# so comments can be added in a "comment" key.
+# 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.
+
+[2c44f7bf-ba20-43f7-a3bf-f2219c0c3f98]
+description = "Empty RNA sequence results in no proteins"
 
 [96d3d44f-34a2-4db4-84cd-fff523e069be]
 description = "Methionine RNA sequence"
@@ -53,6 +63,12 @@ description = "STOP codon RNA sequence 2"
 [9c2ad527-ebc9-4ace-808b-2b6447cb54cb]
 description = "STOP codon RNA sequence 3"
 
+[f4d9d8ee-00a8-47bf-a1e3-1641d4428e54]
+description = "Sequence of two protein codons translates into proteins"
+
+[dd22eef3-b4f1-4ad6-bb0b-27093c090a9d]
+description = "Sequence of two different protein codons translates into proteins"
+
 [d0f295df-fb70-425c-946c-ec2ec185388e]
 description = "Translate RNA strand into correct protein list"
 
@@ -70,3 +86,22 @@ description = "Translation stops if STOP codon in middle of three-codon sequence
 
 [2c2a2a60-401f-4a80-b977-e0715b23b93d]
 description = "Translation stops if STOP codon in middle of six-codon sequence"
+
+[f6f92714-769f-4187-9524-e353e8a41a80]
+description = "Sequence of two non-STOP codons does not translate to a STOP codon"
+
+[1e75ea2a-f907-4994-ae5c-118632a1cb0f]
+description = "Non-existing codon can't translate"
+include = false
+
+[9eac93f3-627a-4c90-8653-6d0a0595bc6f]
+description = "Unknown amino acids, not part of a codon, can't translate"
+reimplements = "1e75ea2a-f907-4994-ae5c-118632a1cb0f"
+include = false
+
+[9d73899f-e68e-4291-b1e2-7bf87c00f024]
+description = "Incomplete RNA sequence can't translate"
+include = false
+
+[43945cf7-9968-402d-ab9f-b8a28750b050]
+description = "Incomplete RNA sequence can translate if valid until a STOP codon"
diff --git a/exercises/practice/protein-translation/test/protein_translation_test.clj b/exercises/practice/protein-translation/test/protein_translation_test.clj
index 05981ed..2b25c7b 100644
--- a/exercises/practice/protein-translation/test/protein_translation_test.clj
+++ b/exercises/practice/protein-translation/test/protein_translation_test.clj
@@ -1,36 +1,138 @@
 (ns protein-translation-test
-  (:require [clojure.test :refer [deftest is are]]
+  (:require [clojure.test :refer [deftest testing is]]
             protein-translation))
 
-(deftest AUG-translates-to-Methionine
-  (is (= "Methionine" (protein-translation/translate-codon "AUG"))))
+(deftest empty-rna-sequence-results-in-no-protein
+  (testing "Empty RNA sequence results in no protein"
+    (is (= [] (protein-translation/translate-rna "")))))
 
-(deftest UGG-translates-to-Tryptophan
-  (is (= "Tryptophan" (protein-translation/translate-codon "UGG"))))
+(deftest methionine-RNA-sequence
+  (testing "Methionine RNA sequence"
+    (is (= ["Methionine"]
+           (protein-translation/translate-rna "AUG")))))
 
-(deftest identifies-Phenylalanine-codons
-  (are [codon] (= "Phenylalanine" (protein-translation/translate-codon codon)) "UUU" "UUC"))
+(deftest phenylalanine-RNA-sequence-1
+  (testing "Phenylalanine RNA sequence 1"
+    (is (= ["Phenylalanine"]
+           (protein-translation/translate-rna "UUU")))))
 
-(deftest identifies-Leucine-codons
-  (are [codon] (= "Leucine" (protein-translation/translate-codon codon)) "UUA" "UUG"))
+(deftest phenylalanine-RNA-sequence-2
+  (testing "Phenylalanine RNA sequence 2"
+    (is (= ["Phenylalanine"]
+           (protein-translation/translate-rna "UUC")))))
 
-(deftest identiefies-Serine-codons
-  (are [codon] (= "Serine" (protein-translation/translate-codon codon)) "UCU" "UCC" "UCA" "UCG"))
+(deftest leucine-RNA-sequence-1
+  (testing "Leucine RNA sequence 1"
+    (is (= ["Leucine"]
+           (protein-translation/translate-rna "UUA")))))
 
-(deftest identiefies-Tyrosine-codons
-  (are [codon] (= "Tyrosine" (protein-translation/translate-codon codon)) "UAU" "UAC"))
+(deftest leucine-RNA-sequence-2
+  (testing "Leucine RNA sequence 2"
+    (is (= ["Leucine"]
+           (protein-translation/translate-rna "UUG")))))
 
-(deftest identifies-Cysteine-codons
-  (are [codon] (= "Cysteine" (protein-translation/translate-codon codon)) "UGU" "UGC"))
+(deftest serine-RNA-sequence-1
+  (testing "Serine RNA sequence 1"
+    (is (= ["Serine"]
+           (protein-translation/translate-rna "UCU")))))
 
-(deftest identifies-stop-codons
-  (are [codon] (= "STOP" (protein-translation/translate-codon codon)) "UAA" "UAG" "UGA"))
+(deftest serine-RNA-sequence-2
+  (testing "Serine RNA sequence 2"
+    (is (= ["Serine"]
+           (protein-translation/translate-rna "UCC")))))
 
-(deftest translates-rna-strand-into-correct-protein
-  (is (= ["Methionine" "Phenylalanine" "Tryptophan"] (protein-translation/translate-rna "AUGUUUUGG"))))
+(deftest serine-RNA-sequence-3
+  (testing "Serine RNA sequence 3"
+    (is (= ["Serine"]
+           (protein-translation/translate-rna "UCA")))))
 
-(deftest stops-translation-if-stop-codon-present
-  (is (= ["Methionine" "Phenylalanine"] (protein-translation/translate-rna "AUGUUUUAA"))))
+(deftest serine-RNA-sequence-4
+  (testing "Serine RNA sequence 4"
+    (is (= ["Serine"]
+           (protein-translation/translate-rna "UCG")))))
 
-(deftest stops-translation-of-longer-strand
-  (is (= ["Tryptophan" "Cysteine" "Tyrosine"] (protein-translation/translate-rna "UGGUGUUAUUAAUGGUUU"))))
+(deftest tyrosine-RNA-sequence-1
+  (testing "Tyrosine RNA sequence 1"
+    (is (= ["Tyrosine"]
+           (protein-translation/translate-rna "UAU")))))
+
+(deftest tyrosine-RNA-sequence-2
+  (testing "Tyrosine RNA sequence 2"
+    (is (= ["Tyrosine"]
+           (protein-translation/translate-rna "UAC")))))
+
+(deftest cysteine-RNA-sequence-1
+  (testing "Cysteine RNA sequence 1"
+    (is (= ["Cysteine"]
+           (protein-translation/translate-rna "UGU")))))
+
+(deftest cysteine-RNA-sequence-2
+  (testing "Cysteine RNA sequence 2"
+    (is (= ["Cysteine"]
+           (protein-translation/translate-rna "UGC")))))
+
+(deftest tryptophan-RNA-sequence
+  (testing "Tryptophan RNA sequence"
+    (is (= ["Tryptophan"]
+           (protein-translation/translate-rna "UGG")))))
+
+(deftest STOP-codon-RNA-sequence-1
+  (testing "STOP codon RNA sequence 1"
+    (is (= [] (protein-translation/translate-rna "UAA")))))
+
+(deftest STOP-codon-RNA-sequence-2
+  (testing "STOP codon RNA sequence 2"
+    (is (= [] (protein-translation/translate-rna "UAG")))))
+
+(deftest STOP-codon-RNA-sequence-3
+  (testing "STOP codon RNA sequence 3"
+    (is (= [] (protein-translation/translate-rna "UGA")))))
+
+(deftest sequence-of-two-protein-codons-translates-into-proteins
+  (testing "Sequence of two protein codons translates into proteins"
+    (is (= ["Phenylalanine", "Phenylalanine"]
+           (protein-translation/translate-rna "UUUUUU")))))
+
+(deftest sequence-of-two-different-protein-codons-translates-into-proteins
+  (testing "Sequence of two different protein codons translates into proteins"
+    (is (= ["Leucine", "Leucine"]
+           (protein-translation/translate-rna "UUAUUG")))))
+
+(deftest translate-RNA-strand-into-correct-protein-list
+  (testing "Translate RNA strand into correct protein list"
+    (is (= ["Methionine", "Phenylalanine", "Tryptophan"]
+           (protein-translation/translate-rna "AUGUUUUGG")))))
+
+(deftest translation-stops-if-STOP-codon-at-beginning-of-sequence
+  (testing "Translation stops if STOP codon at beginning of sequence"
+    (is (= [] (protein-translation/translate-rna "UAGUGG")))))
+
+(deftest translation-stops-if-STOP-codon-at-end-of-two-codon-sequence
+  (testing "Translation stops if STOP codon at end of two-codon sequence"
+    (is (= ["Tryptophan"]
+           (protein-translation/translate-rna "UGGUAG")))))
+
+(deftest translation-stops-if-STOP-codon-at-end-of-three-codon-sequence
+  (testing "Translation stops if STOP codon at end of three-codon sequence"
+    (is (= ["Methionine", "Phenylalanine"]
+           (protein-translation/translate-rna "AUGUUUUAA")))))
+
+(deftest translation-stops-if-STOP-codon-in-middle-of-three-codon-sequence
+  (testing "Translation stops if STOP codon in middle of three-codon sequence"
+    (is (= ["Tryptophan"]
+           (protein-translation/translate-rna "UGGUAGUGG")))))
+
+(deftest translation-stops-if-STOP-codon-in-middle-of-six-codon-sequence
+  (testing "Translation stops if STOP codon in middle of six-codon sequence"
+    (is (= ["Tryptophan", "Cysteine", "Tyrosine"]
+           (protein-translation/translate-rna "UGGUGUUAUUAAUGGUUU")))))
+
+(deftest sequence-of-two-non-STOP-codons-does-not-translate-to-a-STOP-codon
+  (testing "Sequence of two non-STOP codons does not translate to a STOP codon"
+    (is (= ["Methionine", "Methionine"]
+           (protein-translation/translate-rna "AUGAUG")))))
+
+(deftest incomplete-RNA-sequence-can-translate-if-valid-until-a-STOP-codon
+  (testing "Incomplete RNA sequence can translate if valid until a STOP codon"
+    (is (= ["Phenylalanine", "Phenylalanine"]
+           (protein-translation/translate-rna "UUCUUCUAAUGGU")))))
diff --git a/exercises/practice/scrabble-score/test/scrabble_score_test.clj b/exercises/practice/scrabble-score/test/scrabble_score_test.clj
index 1fbd097..09a2f40 100644
--- a/exercises/practice/scrabble-score/test/scrabble_score_test.clj
+++ b/exercises/practice/scrabble-score/test/scrabble_score_test.clj
@@ -1,21 +1,47 @@
 (ns scrabble-score-test
-  (:require [clojure.test :refer [deftest is]]
+  (:require [clojure.test :refer [deftest testing is]]
             scrabble-score))
 
-(deftest lower-case-letter
-  (is (= 1 (scrabble-score/score-letter "a"))))
+(deftest lowercase-letter
+  (testing "Lowercase letter"
+    (is (= 1 (scrabble-score/score-word "a")))))
 
-(deftest upper-case-letter
-  (is (= 1 (scrabble-score/score-letter "A"))))
+(deftest uppercase-letter
+  (testing "Uppercase letter"
+    (is (= 1 (scrabble-score/score-word "A")))))
 
-(deftest two-letter-word
-  (is (= 2 (scrabble-score/score-word "at"))))
+(deftest valuable-letter
+  (testing "Valuable letter"
+    (is (= 4 (scrabble-score/score-word "f")))))
 
-(deftest bigger-word-1
-  (is (= 6 (scrabble-score/score-word "street"))))
+(deftest short-word
+  (testing "Short word"
+    (is (= 2 (scrabble-score/score-word "at")))))
 
-(deftest bigger-word-2
-  (is (= 22 (scrabble-score/score-word "quirky"))))
+(deftest short-valuable-word
+  (testing "Short, valuable word"
+    (is (= 12 (scrabble-score/score-word "zoo")))))
 
-(deftest all-upper-case-word
-  (is (= 41 (scrabble-score/score-word "OXYPHENBUTAZONE"))))
+(deftest medium-word
+  (testing "Medium word"
+    (is (= 6 (scrabble-score/score-word "street")))))
+
+(deftest medium-valuable-word
+  (testing "medium, valuable word"
+    (is (= 22 (scrabble-score/score-word "quirky")))))
+
+(deftest long-mixed-case-word
+  (testing "Long, mixed-case word"
+    (is (= 41 (scrabble-score/score-word "OxyphenButazone")))))
+
+(deftest english-like-word
+  (testing "English-like word"
+    (is (= 8 (scrabble-score/score-word "pinata")))))
+
+(deftest empty-input
+  (testing "Empty input"
+    (is (= 0 (scrabble-score/score-word "")))))
+
+(deftest entire-alphabet-available
+  (testing "Entire alphabet available"
+    (is (= 87 (scrabble-score/score-word "abcdefghijklmnopqrstuvwxyz")))))
diff --git a/exercises/practice/space-age/.meta/tests.toml b/exercises/practice/space-age/.meta/tests.toml
index b4a221d..5e25a02 100644
--- a/exercises/practice/space-age/.meta/tests.toml
+++ b/exercises/practice/space-age/.meta/tests.toml
@@ -1,6 +1,13 @@
-# This is an auto-generated file. Regular comments will be removed when this
-# file is regenerated. Regenerating will not touch any manually added keys,
-# so comments can be added in a "comment" key.
+# 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.
 
 [84f609af-5a91-4d68-90a3-9e32d8a5cd34]
 description = "age on Earth"
@@ -25,3 +32,8 @@ description = "age on Uranus"
 
 [80096d30-a0d4-4449-903e-a381178355d8]
 description = "age on Neptune"
+
+[57b96e2a-1178-40b7-b34d-f3c9c34e4bf4]
+description = "invalid planet causes error"
+include = false
+comment = "excluded because the existing tests use a separate function for each planet"