TARGET PROJECT: webhook — HANDOFF NOTE

Current resolved configuration (superseded and unrelated archived-project settings discarded):
{
  "delete_wins": false,
  "latest_by": "timestamp",
  "scope_key": "global",
  "tie_policy": "last",
  "allow_resurrection": false,
  "amount_mode": "delta",
  "gap_policy": "ignore",
  "precision": 2,
  "inclusive_age": false,
  "max_age": 120,
  "audit_duplicate": true,
  "order": "key"
}

Interface:
evaluate(payload) -> {entities: [{tenant, entity, value: string, deleted: boolean}], duplicates: [event_id]}
- payload has integer `now`; events contain id, tenant, entity, seq, ts, kind ("set"/"delete"), amount as nonnegative decimal string.
- Do not mutate input.

Algorithm contract:
1. Age filter: discard events where now - ts > max_age. With inclusive_age=false, also discard equality (age exactly 120 is discarded).
2. Deduplication: after age filtering, deduplicate by id globally (scope_key="global"), keeping first occurrence. audit_duplicate=true: append each duplicate event_id to `duplicates`, preserving encounter order.
3. Group retained events by (tenant, entity).
4. Sorting/ties: latest_by="timestamp" means sort field is ts. For ties on ts, retain the last event per tie_policy="last". Process retained events in ascending ts order.
5. Per-group processing: initialize 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.
   - Delete: sets deleted=true.
   - Set: updates only if not deleted, or if allow_resurrection (currently false, so no resurrection after delete). When it updates, it resets deleted=false. amount_mode="delta": adds amount to value (does not assign).
6. delete_wins=false: no final forced deletion; final deleted reflects normal processing only (the general rule, when enabled, would force deleted=true if any retained event is a delete even if gap-skipped, without changing value — currently inactive).
7. Output value: round to precision=2 decimal places using exact ROUND_HALF_UP; return fixed-point string with exactly 2 digits.
8. Entity ordering: order="key" — sort entities by (tenant, entity). (The alternative "input" ordering, by earliest retained original index in each group before tie filtering, is not current.)

Exceptions / unresolved items: None outstanding. All 12 configuration keys above are resolved at their current values.