WEBHOOK PROJECT HANDOFF NOTE

Scope: only the target project "webhook". All "archived work records" name other projects (archive-*) and were discarded. None of their settings apply here.

CURRENT CONFIG (after Updates 1-5, merged)
- scope_key: "global" (dedupe by event id across all tenants; Update 1)
- latest_by: "timestamp" (sort by ts; Update 1)
- tie_policy: "last" (on equal sort values, retain the last event; Update 1)
- delete_wins: false (Update 1)
- allow_resurrection: false (Update 2 set true, Update 4 superseded it)
- amount_mode: "delta" (Update 2 set absolute, Update 4 superseded it)
- gap_policy: "ignore" (Update 2 set accept, Update 5 superseded it)
- precision: 2 (Update 2 set 0, Update 5 superseded it)
- inclusive_age: false (Update 3)
- max_age: 120 (Update 3)
- audit_duplicate: true (Update 5)
- order: "key" (Update 5)

All 12 keys are resolved. Nothing was left at an unspecified default.

FIXED CONTRACT (from the task statement)
evaluate(payload) -> {entities:[{tenant,entity,value:string,deleted:boolean}], duplicates:[event_id]}
- Input: now (integer). Events have id, tenant, entity, seq, ts, kind (set/delete), and amount (nonnegative decimal string).
- Age filter: discard an event if now-ts > max_age. Also discard it when equal, unless inclusive_age. With the current config, age >= 120 is discarded and age 119 is kept.
- Dedupe the remaining events by id globally (scope_key=global), keeping the first. Append the duplicate id to duplicates, in encounter order, iff audit_duplicate (currently true).
- Group by (tenant, entity).
- Sort field is ts. Ties keep the last event (tie_policy=last). Process the retained events in ascending ts.
- Initial state: seq=0, value=0, deleted=false.
- gap_policy=ignore: process an event only when event.seq == previous processed seq + 1. Skipped events do not change seq.
- Any processed event updates seq to the event's seq.
- Delete sets deleted=true.
- Set updates only if not deleted or allow_resurrection. Currently allow_resurrection=false, so a set after a delete is ignored. A set that does apply resets deleted=false and, in delta mode, adds the amount.
- delete_wins would force final deleted=true if any retained event is a delete, even if skipped by the gap filter. It does not change value. It is currently false, so it has no effect.
- Final value is rounded to precision decimal places (2) with exact ROUND_HALF_UP, as a fixed-point string with exactly 2 decimals. Use decimal arithmetic, not floats.
- Entity ordering: order=key sorts by (tenant, entity). The alternative, earliest retained original index before tie filtering, is not in use.
- duplicates preserves encounter order. Do not mutate the input.

UNRESOLVED / NOTES
- No open requirement conflicts were found.
- Ambiguities to confirm if they arise:
  - Whether the age filter and dedupe use the original input order for "first".
  - Whether the tie filter applies before gap processing (the contract implies yes).
  - Whether a set skipped for deleted state still advances seq (the contract implies yes, since any processed event updates seq).
- Updates after Update 5 are not yet available. Any later update replaces only the keys it names.