HANDOFF NOTE - Target project: invoice (only authoritative project)

Current configuration (final, after all updates; superseded values discarded):
{
  "currency": "CAD",
  "fee_refundable": false,
  "min_invoice_cents": 10,
  "status_case": "lower",
  "rounding": "per_line",
  "duplicate_event": "first",
  "inclusive_limit": true,
  "max_refund_age_days": 30,
  "refunds_first": true,
  "refund_overflow": "cap",
  "void_is_tombstone": true,
  "order": "id"
}

Fixed interface:
- Implement evaluate(payload) -> {invoices: [{id, charge, refunded, balance, status}]}
- Empty input produces {invoices: []}. Do not mutate payload.
- Input invoices: unique id, integer created day, currency, nonnegative decimal-dollar strings in lines, nonnegative fee_cents, ordered events with eid, kind refund/void/restore, day and nonnegative cents.

Algorithm contract:
- Filter out invoices whose currency is not CAD.
- Convert each line to cents using exact decimal ROUND_HALF_UP. Rounding is per_line: round each line before summing. Principal is that sum. Charge = principal + fee_cents.
- Skip invoices with charge below min_invoice_cents = 10.
- Deduplicate events by eid in first mode: keep first occurrence/value and its position; later duplicates ignored.
- Event ordering: refunds_first = true, so stably move refund events ahead of all other events, preserving relative order within groups.
- Start active. A void disables future refunds. Because void_is_tombstone = true, a void also permanently prevents any subsequent restore from re-enabling refunds. Earlier refunds are never undone.
- A refund applies only while active and when event day minus created is nonnegative and within the limit: with inclusive_limit = true, age <= 30 days (age 30 allowed, negative age not allowed).
- Refund ceiling: principal plus fee only if fee_refundable. Since fee_refundable = false, ceiling is principal only (fee excluded).
- Refund overflow is cap: cap an oversized refund at the unrefunded ceiling amount rather than rejecting the whole event.
- End status, in lower case: "void" if inactive at end, else "refunded" if refunded equals charge, else "open". status_case = lower.
- balance = charge - refunded.
- Sort output invoices by id.

Unresolved items: None. All 12 configuration keys above are resolved to current values.

Excluded: All archived-work-record projects and their settings are unrelated to invoice and were discarded.