HANDOFF NOTE: project "invoice" (target only)

1. Interface and algorithm contract (fixed, unchanged)
evaluate(payload) -> {invoices:[{id, charge, refunded, balance, status}]}
- Invoice input: unique id, integer created day, currency, lines (nonnegative decimal-dollar strings), nonnegative fee_cents, ordered events (eid, kind in refund/void/restore, day, nonnegative cents).
- Skip invoices whose currency differs from the configured currency.
- Convert each line to cents with exact decimal ROUND_HALF_UP. per_line rounds each line and then sums. invoice sums first and then rounds.
- charge = principal cents + fee_cents.
- Skip invoices where charge < min_invoice_cents.
- Deduplicate events by eid. The key keeps its first insertion position. For duplicate_event "last", the value is replaced by the later event. For "first", the first value is kept.
- refunds_first true: stably move refund events ahead of all other events. false: keep the original order.
- Processing starts in the active state.
  - A void disables future refunds.
  - With void_is_tombstone true, a void also permanently blocks later restores.
  - With void_is_tombstone false, a restore re-enables refunds.
  - Earlier refunds are never undone.
- A refund applies only while active and when age = event day - created is nonnegative and below max_refund_age_days. Age equal to the limit is allowed when inclusive_limit is true.
- Refund ceiling = principal, plus fee if fee_refundable is true.
  - refund_overflow "cap": cap the refund at the unrefunded ceiling.
  - refund_overflow "reject": reject the whole oversized event.
- Status: "void" if inactive at the end; else "refunded" if refunded == charge; else "open". status_case controls the case.
- balance = charge - refunded.
- Sort invoices by id or by (created, id), per order.
- Do not mutate the payload. Empty input gives invoices: [].

2. CURRENT effective settings (after Updates 1-5)
- rounding = "per_line"
- duplicate_event = "first"
- inclusive_limit = true
- max_refund_age_days = 30
- order = "id" (sort by id)
- refund_overflow = "cap"
- refunds_first = true
- void_is_tombstone = true
- min_invoice_cents = 10
- status_case = "lower" (statuses: "void", "refunded", "open")
- currency = "CAD"
- fee_refundable = false (refund ceiling is principal only)

Every key has a value, so no setting is currently unresolved.

3. Superseded values (do not use)
- Update 2 set order=created, refund_overflow=reject, refunds_first=false and void_is_tombstone=false. Updates 4 and 5 replaced these.
- Updates 1 and 3 values remain current. Their keys were not changed afterwards.

4. Exceptions and unresolved items
- There are no per-invoice exceptions.
- Ambiguities the contract does not settle; choose a reasonable behavior and document it:
  - Whether the charge threshold compares against charge including the fee. The contract says "charge below min", so it does, with the fee.
  - How a refund event is handled when its cents are 0 or the ceiling is already reached under "cap". It is likely a no-op.
  - What to do with unknown event kinds.
  - Whether the refunds_first reordering happens after deduplication. The contract order suggests it does.
  - How the void state interacts with a restore that occurs before any void. It is likely a no-op.

5. Ignored material
Archive records for other projects (archive-west, north, east and south series) are unrelated and were discarded. Their settings were never applied to invoice.

6. Next steps
Apply any future updates as key-level replacements over the current settings above. Then implement and test against the boundary cases: half-up rounding, the age limit at exactly 30 days, duplicate eids, tombstone voids, the cap, and the fee excluded from the ceiling.