import: give the upload's empty file list a type
Steep refuses a bare `[]` with nothing to read a type off. The parts
move behind #files_in, where the signature says what none of them is
— #ids_in's reason for being a method rather than a ternary.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KrREeoPLXx1qsYYq7qkL8h
change
commit 586ed58c023d908b53f95efc6268342c2b2bf896
author Claude <noreply@anthropic.com>
date
parent 04f03cd6
diff --git a/lib/pro_tacts/web/import.rb b/lib/pro_tacts/web/import.rb
index b8dbf32..d639af6 100644
--- a/lib/pro_tacts/web/import.rb
+++ b/lib/pro_tacts/web/import.rb
@@ -41,8 +41,7 @@ module ProTacts
     # no other page that holds what just arrived.
     #: (untyped r) -> String
     def apply_import(r)
-      uploads = r.params["cards"]
-      uploads = uploads.is_a?(Array) ? uploads.select { it.is_a?(Hash) && it[:tempfile] } : []
+      uploads = files_in(r.params["cards"])
       return plan_upload_screen(notice: "Choose the card files in a plan's cards directory.") if uploads.empty?
 
       cards = {} #: Hash[String, Import::Card]
@@ -74,6 +73,15 @@ module ProTacts
       plan_upload_screen(arrivals: Import::Land.call(store, cards))
     end
 
+    # The file parts the form sent, and none for a POST carrying no
+    # file: #ids_in's shape over an upload, and its reason for being a
+    # method — the empty case needs a type, and the signature is where
+    # one fits.
+    #: (untyped param) -> Array[untyped]
+    def files_in(param)
+      param.is_a?(Array) ? param.select { it.is_a?(Hash) && it[:tempfile] } : []
+    end
+
     #: (?arrivals: Array[Import::Land::Arrival]?, ?notice: String?) -> String
     def plan_upload_screen(arrivals: nil, notice: nil)
       response["Content-Type"] = "text/html; charset=utf-8"