When no explicit slot-to-tray mapping is captured (path 5 of 6 in
_track_from_3mf — fires before the request-topic subscription that catches
ams_mapping is accepted), the tracker builds available_trays from
build_ams_tray_lookup and uses position to map the slicer's Nth filament
to the Nth available tray. The helper enumerated every AMS tray by id
regardless of whether a spool was loaded, so AMS slots 0-2 loaded + slot 3
empty + external yielded available_trays = [0, 1, 2, 3, 254]. The slicer
compacts its filament UI to hide empty AMS slots, so its 4th filament is
the external — but position mapping routed it to AMS0-T3 (the empty slot)
instead of 254 (external). No spool assigned there → usage silently
skipped → external never decremented.
Filter the fallback to slots with a non-empty tray_type. build_ams_tray_lookup
stays unchanged for its other callers (spoolman_tracking.store_print_data,
routes/printers, spool_assignment_notifications); the filter is applied at
the usage-tracker call site only. Mirrors the existing vt_tray filter in
build_ams_tray_lookup line 174.