Ack! Caught a mistake in the draft code (I’d renamed some variables
mid-way but apparently didn’t fix all occurrences). It should go

\cs_new_protected:Npn \__keys_filters_set:n #1
  {
    \clist_clear:N \l__key_filters_clist
    \clist_map_inline:nn {#1}
      {
        \prop_if_in:NoTF \l__filter_states_prop { \l__keys_module_tl / ##1 }
          {
            \clist_put_right:No \l__key_filters_clist
              { \l__keys_module_tl / ##1 }
          }
          {
            \__msg_kernel_error:nnoVV { kernel } { invalid-filter }
              { \l__keys_module_tl / ##1 } \l_keys_path_tl \l__keys_module_tl
          }
      }
    \clist_if_empty:NF \l__key_filters_clist  % VARIABLE RENAMED HERE
      {
        \prop_put:NVV \l__keys_filters_prop
          \l_keys_path_tl \l__key_filters_clist
      }
  }

The several l__tmpX_Y variables I use should also be renamed; for some
reason I thought those were defined in addition to the l_tmpX_Y
versions.