# Exercise 16. An extension moves the term, not cancels the duty An exercise for the [duty](/tutorials/duty/) page. On the page an extension removes the document-return duty, which is imprecise: an extension grants a **new** term, and the duty lives until it. **Setup.** Replace the "extension granted" fact with a dated "extended until" fact, and write a second duty: return the document by the new date. The overdue fine must count from the new date. Expectations: with an issue due 31 March and an extension to 30 April, a 5 May non-return yields a fine, a 15 April non-return does not; on 10 April the duty to return by the extended term is active, the original one is not. **Hint.** The `unless` clause must bind the extension date itself. The fine reads two dates, and the comparison goes against the later one. Below is the solution. Try it yourself first. ## Solution ```law language "law.core" version "0.2"; package tutorial.archive version "0.5.1"; namespace "urn:law:tutorial:archive"; entity Person; relation document_lent(p: Person, issued: Date, due: Date) kind empirical { key(p); } relation document_returned(p: Person) kind empirical; relation extension_granted_until(p: Person, until: Date) kind empirical { key(p); } relation non_return_recorded(p: Person, on: Date) kind empirical { key(p); } relation extension_granted(p: Person) kind institutional; relation late_fee_due(p: Person) kind institutional; ``` The original duty is removed by the extension, as on the page, but `unless` now reads the date: ```law rule ReturnRule defeasible { for p: Person; for issued, due, until: Date; when document_lent(p, issued, due); then duty Return { bearer p; goal achievement { condition document_returned(p); window [issued, due]; } }; unless document_lent(p, issued, due) and extension_granted_until(p, until); } ``` The second duty, with a window to the new date: ```law rule ReturnAfterExtension strict { for p: Person; for issued, due, until: Date; when document_lent(p, issued, due) and extension_granted_until(p, until); then duty ReturnExtended { bearer p; goal achievement { condition document_returned(p); window [issued, until]; } }; } rule NonReturnRecorded strict { for p: Person; for on: Date; when non_return_recorded(p, on); then not document_returned(p); } ``` The fine is two norms, one per term. Without an extension it counts from the original term; with one from the new, and the first norm stays silent under an extension. For the first norm to ask "no extension", the dated fact folds into a dateless flag: ```law rule ExtensionExists strict { for p: Person; for until: Date; when extension_granted_until(p, until); then extension_granted(p); } rule LateReturnFee strict { for p: Person; for issued, due, on: Date; when document_lent(p, issued, due) and non_return_recorded(p, on) and on > due and not_known(extension_granted(p)); then late_fee_due(p); } rule LateReturnFeeAfterExtension strict { for p: Person; for issued, due, until, on: Date; when document_lent(p, issued, due) and extension_granted_until(p, until) and non_return_recorded(p, on) and on > until; then late_fee_due(p); } ``` The first test, on the fine past the extended term: ```law test "невозврат после продлённого срока — штраф" { given { context { decision_time @2026-06-01T09:00:00+05:00; knowledge_time @2026-06-01T09:00:00+05:00; legal_time @2026-05-10; timezone "Asia/Almaty"; } assert document_lent(entity_ref("urn:tutorial:ivanova"), @2026-03-01, @2026-03-31) { id "assert-lent"; origin case_input; } assert extension_granted_until(entity_ref("urn:tutorial:ivanova"), @2026-04-30) { id "assert-extension"; origin case_input; } assert non_return_recorded(entity_ref("urn:tutorial:ivanova"), @2026-05-05) { id "assert-non-return"; origin case_input; } } evaluate truth(late_fee_due(entity_ref("urn:tutorial:ivanova"))); expect truth_status == TRUE_ONLY; expect evaluation_status == COMPUTED; } ``` The test name reads: "Non-return past the extended term — a fine." ## Check Three tests in `tests/46-exercise-duty.lawtest`: the fine past the new term, positions on 10 April (`ReturnExtended` active, `Return` gone), and a pre-new-term non-return without a fine. The positions test is shown not on the page but in the file: the `position(...)` observation does not compile into the package. Why the `extension_granted` flag. Asking `not_known(extension_granted_until(p, until))` will not do: `until` is bound by nothing in this norm, and with `due` substituted the question would become "no extension to exactly the old term", which an extension to any other date would not defeat. This page's first version was written exactly so, and the third test caught it: the fine accrued on a 15 April non-return, inside the extended term. What the exercise teaches. An extension is not a cancellation but a new duty with a new window, and the fine must follow the window. Positions show this more directly than facts: on 10 April one duty is active, the other removed.