# description
Schema for docs/salesperson_map.json — manual mapping between STORIS short codes (TC, AW1, KB, ...) and Trakwell longer codes (TCROSIER, AWELLS, KBEAVERS, ...). Required for fusion phase 5 (per-salesperson close ratio). Stale entries silently corrupt KPIs; this schema enforces shape but cannot enforce currency. See docs/CONCEPTS.md for vocabulary; docs/EXTERNAL_RESEARCH_INSIGHTS.md §3.4 for the canonical mapping origin.

# properties._last_synced_at.description
ISO date (YYYY-MM-DD) the resolver most recently produced this file. Written by pipeline/sync_identity_from_sheet.py.

# properties._last_synced_window_days.description
Trakwell salestatsemp activity window the resolver used (default 90 days). Affects which Trakwell-only ghosts surface in the drift report and what trakwell-presence signals contributed to last_known_active_at.

# $defs.SalespersonEntry.properties.trakwell_code.description
Null when no Trakwell counterpart is documented (e.g. system accounts, unmapped upper managers).

# $defs.SalespersonEntry.properties.full_name.description
Display name. Often known but not always — null is acceptable for entries we have a code for but no name yet.

# $defs.SalespersonEntry.properties.also_works_at.description
Other STORIS stores this salesperson works at. Empty for the common case (single-store rep). Decatur outlet shared staff (003+004) populate this.

# $defs.SalespersonEntry.properties.last_known_active_at.description
ISO date (YYYY-MM-DD) the human was last known to be employed/active in any of: STORIS DW transactions, Trakwell salestatsemp, STORIS NextGen lastLogin, HR-sheet presence. Used by phase-5 filters to draw the boundary between real recent work and post-termination ghost-attribution: a BtaData row whose transactionDate > last_known_active_at is treated as ghost-attribution. Only set when active=false AND role=inactive; absent on currently-active entries.

# $defs.SalespersonEntry.properties.name_aliases.description
Historical (first, last) name pairs the human has been known by, across systems and over time. Used by the resolver as a fallback/secondary join when STORIS short code and Trakwell employee_code don't match across systems. Covers nicknames (DW 'AUGGIE' vs sheet 'Auggie'), marital name changes (Andrea Levy → Andrea Smith), and cross-vendor casing/punctuation variations. Aliases should be unique after normalization (uppercase, strip non-alphanumeric, collapse whitespace); the resolver normalizes before comparison so 'O\'Brien' and 'OBRIEN' are treated as the same alias.

# $defs.SalespersonEntry.allOf[].title
If role is 'inactive', active must be false

# $defs.SalespersonEntry.allOf[].title
last_known_active_at is only valid on inactive entries

# $defs.SalespersonEntry.allOf[].title
System accounts (ZZZ, ASH) carry role='system'