Handoff: target project webhook

Authority
- Only webhook requirements apply. All archived records concern unrelated projects and must be ignored.
- Updates 1–5 have been applied in order. Superseded values are discarded.
- No later update or question is available yet.

CURRENT choices
{
  "delete_wins": true,
  "latest_by": "sequence",
  "scope_key": "tenant",
  "tie_policy": "first",
  "allow_resurrection": true,
  "amount_mode": "absolute",
  "gap_policy": "accept",
  "precision": 0,
  "inclusive_age": true,
  "max_age": 60,
  "audit_duplicate": false,
  "order": "input"
}

Fixed interface
evaluate(payload) returns:
{
  "entities": [
    {"tenant": ..., "entity": ..., "value": "fixed-point string", "deleted": boolean}
  ],
  "duplicates": [event_id, ...]
}

Input contract
- now is an integer.
- Events contain id, tenant, entity, seq, ts, kind ("set" or "delete"), and amount (nonnegative decimal string).
- Do not mutate input.

Algorithm with CURRENT choices
1. Age filter:
   - Discard events when now - ts > 60.
   - Keep events exactly 60 old because inclusive_age=true.
   - The contract provides no separate rejection rule for future timestamps.

2. Deduplicate surviving events:
   - Key is (tenant, id), not id globally.
   - Keep the first encountered event for each key.
   - audit_duplicate=false, so duplicates is always [].
   - Age filtering occurs before deduplication.

3. Group deduplicated events by (tenant, entity).
   - Record each group's earliest retained original input index BEFORE tie filtering; this determines entity output order.

4. Tie filtering and processing order:
   - latest_by="sequence" selects event.seq, not ts.
   - For equal seq within a group, retain the first event in encounter order.
   - Process the retained events in ascending seq order.

5. Initialize each group:
   - Previous processed seq=0, value=0, deleted=false.
   - gap_policy="accept": process all retained events, regardless of sequence gaps.
   - Every processed event updates the previous processed seq.
   - Delete sets deleted=true without changing value.
   - Set is permitted even after deletion because allow_resurrection=true.
   - Set resets deleted=false and assigns amount because amount_mode="absolute"; do not accumulate amounts.

6. Delete precedence:
   - delete_wins=true forces final deleted=true if any retained event is a delete.
   - This final override does not change value.
   - Thus a later set may update value and temporarily resurrect the entity, yet the final result remains deleted if a retained delete exists.
   - Deletes removed by age filtering, deduplication, or tie filtering do not count as retained deletes.

7. Final value:
   - Round only at the end using exact ROUND_HALF_UP.
   - precision=0: return a fixed-point integer string, with no decimal point or fractional digits.
   - Use exact decimal arithmetic, not binary floating-point rounding.

8. Output ordering:
   - order="input": sort entities by their groups' earliest retained original index from before tie filtering.
   - Do not sort entities by tenant/entity keys.
   - The general contract requires duplicate IDs, when auditing is enabled, to preserve encounter order.

Unresolved / continuation
- All 12 choice keys above have explicit current values; no choice remains unresolved.
- Implementation language, input validation/error behavior, and runtime environment are not specified.
- No implementation or execution is included in this handoff.
- Apply any future webhook update only to its named keys, leaving other current choices unchanged. Preserve the fixed interface and algorithm contract unless explicitly revised.