diff --git a/EXERCISE.md b/EXERCISE.md
new file mode 100644
index 0000000..1889365
--- /dev/null
+++ b/EXERCISE.md
@@ -0,0 +1,151 @@
+# Exercise: the discount that wouldn't map
+
+You are in the **"after" situation** of this code base: the domain objects
+(`Client`, `Order`) are plain — no `DbBase` inheritance, no knowledge of the
+`DbContext`. Two hand-written mapping families lead out of the domain:
+
+- `OrderDto` / `ClientDto` — transport shapes consumed by the WebApp;
+- `OrderData` / `ClientData` — the data-only records the persistence framework
+ stores (one row per object, object graph flattened to ids).
+
+The *before* situation was Active Record: the domain object and the stored
+shape were one and the same class, so there was nothing to map. Now mapping is
+explicit — and that is where this exercise lives.
+
+## What you get
+
+- `Order.TotalInCents` — the order total, an integer in cents, persisted and
+ mapped one-on-one into `OrderDto` and `OrderData`.
+- `DiscountCalculator.CalculateDiscount(order, discountBarrierCents, discountPercentage)` —
+ a pure domain service. It returns a **`DiscountedOrder`** (a separate domain
+ type, correlated to the order by `OrderId`) carrying the order's total, the
+ percentage *actually applied* (0 when the total does not strictly exceed the
+ barrier), and the discounted total. Rounding is half-up, in integer math.
+- Unit tests for all of the above already pass.
+
+## The challenge
+
+The `DiscountedOrder` is **not** a one-on-one copy of the order, and it must
+not become one:
+
+- `Order` needs to learn the *discounted total* — but neither `OrderDto` nor
+ `OrderData` may gain it. The discount is derived data: it lives in the
+ domain only, and the storage schema stays untouched.
+- The `DiscountedOrder.DiscountPercentage` is **not** copied onto the order —
+ it can be derived from the two totals (and `TotalInCents` never changes).
+
+So the flow is: order → `DiscountedOrder` (calculate) → order (correlate +
+copy, dropping what is derivable) → persistence (which stores *less* than the
+domain now knows).
+
+Work through the three steps below — each is small enough to hand to a coding
+agent as a single instruction. Run the tests after each step:
+
+```sh
+scripts/run-tests.sh
+```
+
+## Step 1 — teach the order its discounted total
+
+Add a single property `DiscountedTotalInCents` (`int`, default 0) to the
+**domain** `Order` in `src/After/Order.cs`. Do not touch `OrderDto`,
+`OrderData`, or the mappers: the discounted total must exist in the domain
+only. Acceptance: the solution builds and the full test suite still passes.
+
+## Step 2 — correlate a discounted order onto its order
+
+Create `tests/BeforeAfter.Tests/DiscountedOrderMappingTests.cs` from the stub
+below and make the **first** test pass. The point is the correlation: the
+`DiscountedOrder` knows the id of the order it was calculated from — use it
+instead of assuming there is only one order around.
+
+```csharp
+using After;
+
+namespace BeforeAfter.Tests;
+
+///
+/// EXERCISE — the mapping from a DiscountedOrder onto an Order is NOT
+/// one-on-one. Make these tests pass, following EXERCISE.md step by step.
+///
+public class DiscountedOrderMappingTests
+{
+ [Fact] // EXERCISE STEP 2
+ public void A_discounted_order_can_be_correlated_onto_its_order()
+ {
+ var db = new DbContext();
+ var client = new Client { Name = "Acme" };
+ var order = new Order { Description = "big order", TotalInCents = 150_00 };
+ client.Orders.Add(order);
+ db.Save(client);
+ db.Save(order);
+
+ // 10 % for orders above €100:
+ var discounted = DiscountCalculator.CalculateDiscount(
+ order, discountBarrierCents: 100_00, discountPercentage: 10);
+
+ // TODO (step 2): correlate the discounted order to the saved order and
+ // copy the discounted total onto the order.
+ // Hints: DiscountedOrder.OrderId identifies the order; the discount
+ // percentage is deliberately NOT copied — it is derivable.
+
+ // ... your code here ...
+
+ Assert.Equal(135_00, order.DiscountedTotalInCents);
+ }
+
+ [Fact] // EXERCISE STEP 3
+ public void Saving_a_discounted_order_keeps_the_stored_total_untouched()
+ {
+ var db = new DbContext();
+ var client = new Client { Name = "Acme" };
+ var order = new Order { Description = "big order", TotalInCents = 150_00 };
+ client.Orders.Add(order);
+ db.Save(client);
+ db.Save(order);
+
+ var discounted = DiscountCalculator.CalculateDiscount(
+ order, discountBarrierCents: 100_00, discountPercentage: 10);
+
+ // TODO (step 3): correlate + copy as in step 2, then save the updated
+ // order through the context. Afterwards find the order's persisted
+ // OrderData row (hint: db.Find(...)) and assert that the
+ // stored total is STILL the original 150_00: the database knows
+ // nothing about discounts — derived data lives in the domain only.
+
+ // ... your code here ...
+
+ OrderData row = null!; // TODO (step 3): find the OrderData row for this order
+ Assert.Equal(150_00, row.TotalInCents);
+ }
+}
+```
+
+Acceptance: the first test is green, the second still red.
+
+## Step 3 — save, and check what actually persisted
+
+Make the **second** test pass: re-save the updated order through the context
+(the context refreshes the stored row in place), then assert on the persisted
+`OrderData` row. Note what you are *not* asserting: there is no discounted
+column to check — the row simply keeps the original total.
+
+Acceptance: the whole suite is green — including the pre-existing tests, which
+guard exactly the invariants you must not break (the DTO and the persistence
+records still map the same fields as before).
+
+## Facilitator notes
+
+- Expected end state per step: (1) one new domain-only property; (2) a mapping
+ fragment *outside* the `Mappers` class — correlation by id plus a field
+ copy, dropping the derivable percentage; (3) `db.Save(order)` after which
+ the `OrderData` row still shows `TotalInCents == 150_00`.
+- Discussion points: why two mapping families out of the domain (transport vs
+ persistence) and why neither carries the discount; what an Active Record
+ "before" world would have done here (the discounted value would have been
+ a column, or nothing at all); what happens to `DiscountedTotalInCents` when
+ the order travels to the WebApp and back (the DTO round-trip silently drops
+ it — derived data must be recomputed, not transported).
+- The unit tests in `DiscountCalculatorTests.cs` pin the calculation contract
+ (strict barrier, half-up rounding, validation); participants should not need
+ to touch them.