/**
 * A Sanity reference to an `ailf.team` document.
 *
 * Members of an array (`AilfUser.teams[]`) carry a `_key`; the single-valued
 * `preferences.primaryTeam` does not. The team slug downstream consumers need
 * is a derived, read-time value from a GROQ projection — never stored here.
 */
export interface TeamReference {
    _type: "reference";
    _ref: string;
    _key?: string;
}
/**
 * Per-user UI preferences. Room to grow (default view, density, …) — kept
 * minimal for v0 (YAGNI).
 */
export interface AilfUserPreferences {
    /**
     * Reference to the user's default team — one of `AilfUser.teams[]`. Distinct
     * from `teams[]` so "which team's view do I default to" can differ from "all
     * teams I affiliate with". The slug is derived in GROQ at read time.
     */
    primaryTeam?: TeamReference;
    /**
     * The team the user most recently dispatched an eval as: a usage signal for
     * auto-selecting a default, not a declaration of affiliation. Unlike
     * `primaryTeam` it carries **no "must be one of `teams[]`" invariant**:
     * dispatching an eval for a team records a last-used default; `teams[]` stays
     * Settings-only. Drives the new-eval team picker's auto-select for users who
     * never open Settings (D0075). The slug is derived in GROQ at read time.
     */
    lastTeam?: TeamReference;
}
/**
 * Per-human user document. Created at the deterministic `_id`
 * `ailf.user.${sanityUserId}` but resolved on a stable key (`email` +
 * `sanityUserIds[]`), because a Sanity seat-id is stable per **account/seat**,
 * not per **human**: an SSO login is a separate seat, so one person can hold
 * several ids over time (D0075 supersedes D0056's "id is the sole key"
 * premise). Stores self-declared team affiliation (references to `ailf.team`)
 * plus UI preferences, and is the primary source for dashboard
 * personalization. Stores minimal PII: seat-ids, `email`, and `displayName`
 * only.
 *
 * @see docs/design-docs/user-identity-resolution.md
 * @see docs/decisions/D0075-canonical-user-identity-resolution.md
 */
export interface AilfUser {
    /** Deterministic at create time: `ailf.user.${sanityUserId}`. */
    _id: string;
    _type: "ailf.user";
    /**
     * The create-time seat-id and legacy scalar key (equals the `_id` segment).
     * Retained for back-compat (it is the create target that keeps the
     * same-session race-safety D0056 relied on); the resilient read key is
     * `sanityUserIds[]` + `email`, not this scalar.
     */
    sanityUserId: string;
    /**
     * Every Sanity seat-id known to map to this human. The seat→human map all of
     * attribution, personalization, and the roster ride on: the upsert unions
     * each session's `CurrentUser.id` in here, so a future SSO seat flip rolls up
     * automatically. The read path normalizes a legacy document carrying only the
     * scalar `sanityUserId` to `[sanityUserId]`, so resolution by `sanityUserIds`
     * works uniformly before any document is rewritten (D0075).
     */
    sanityUserIds: string[];
    /** Denormalized for display / joins (lowercased at write time). */
    email: string;
    /** `CurrentUser.name` snapshot. */
    displayName?: string;
    /** Self-declared affiliation — drives personalization only. */
    teams: TeamReference[];
    preferences: AilfUserPreferences;
    /** ISO 8601 UTC — stamped on each save. */
    updatedAt: string;
    /**
     * ISO 8601 UTC. Most recent dashboard login/session, throttled to once per
     * UTC day by the login trigger (`markSeen`). Distinct from `updatedAt`, which
     * is stamped on any record write. Optional and go-forward: records predating
     * this field, or only ever written by a non-login trigger, may not carry it.
     */
    lastSeenAt?: string;
    /**
     * ISO 8601 UTC. When the user completed (or skipped) the first-login
     * onboarding prompt — "defined = onboarded". Stamped once by the onboarding
     * trigger (`markOnboarded`) and never overwritten, so it records the original
     * completion time. Its presence is the signal the onboarding gate reads to
     * decide whether to show the prompt. Optional and go-forward: records
     * predating onboarding never carry it and are treated as not-yet-onboarded.
     */
    onboardingCompletedAt?: string;
    /**
     * ISO 8601 UTC. When the user completed or skipped the guided product tour.
     * Stamped once by the tour controller on finish/skip and never overwritten.
     * Record-only: auto-run is chain-driven off onboarding completion, so this
     * field is not read for auto-run control flow (it backs analytics and the
     * Settings replay-button label). Optional and go-forward.
     */
    tourSeenAt?: string;
}
