TARGET PROJECT: importer — HANDOFF NOTE

Scope: Only project "importer" is authoritative. All archived records for other projects (archive-*) are unrelated and discarded. Superseded importer values are discarded; below are current choices only.

CURRENT CONFIGURATION:
{
  "duplicate_policy": "first",
  "fold_case": true,
  "tombstone_wins": true,
  "trim_ids": true,
  "list_merge": "replace",
  "nested_merge": true,
  "null_mode": "ignore",
  "version_floor": 2,
  "audit_all": true,
  "empty_id": "skip",
  "order": "input",
  "unknown_fields": "drop"
}

FIXED INTERFACE:
Implement evaluate(payload) -> {records:[{id,fields}], audit:[string]}
Events have: id string, integer version, deleted boolean, fields JSON object.
No mutation of input.

ALGORITHM CONTRACT:
- Normalize id: trim if trim_ids, then ASCII case folding iff fold_case. Current: both enabled.
- Empty normalized id: skip (empty_id="skip"; alternative would be ValueError). Normalize/empty-check before discarding events with version below version_floor (current floor=2).
- Deduplicate by normalized (id, version), retaining first insertion position but selecting event per duplicate_policy. Current policy="first". Sorting uses the selected event's actual original index.
- Process events by (version, selected original index).
- Save each normalized id's first processed index for later ordering.
- Deletion removes a record; with tombstone_wins=true, deletion also permanently blocks later non-deletions for that id.
- A deletion is applied only if a record existed or it installs a new sticky tombstone; redundant deletions are blocked/no-change.
- Otherwise merge fields, creating {} if needed.
- Null values: null_mode="ignore" — ignore nulls (do not delete existing keys).
- nested_merge=true: recursively merge two objects; otherwise object would be replaced.
- Arrays: list_merge="replace" — replace arrays. ("unique" alternative would concatenate only items not structurally equal to any existing old-array item, with no deduplication among new-array items themselves; structural JSON equality would apply. Not current.)
- unknown_fields="drop": filter top-level fields to name, tags, meta only.
- Audit: audit_all=true, so record every processed event, not only applied ones. Format: normalized_id:version:applied or normalized_id:version:blocked. Preserve audit processing order.
- Final records order: order="input" — sort by first processed index, then id. (Alternative "id" would sort lexically by normalized id; not current.)

UNRESOLVED ITEMS: None recorded for importer. No exceptions beyond the rules above.