fix(685): close the second ingress; stop the error path claiming a short query

Verification round returned MERGEABLE with all three blocking findings resolved
by measurement. These are its four remaining items.

- DEFAULT_SEARCH_KINDS was the SECOND ingress into the searched kinds and was
  not derived, so the previous commit's "enforced by the type system" claim held
  for one of two paths. A non-addable kind there typechecked clean and would
  have overstated the hint with every one of its rows dropped — the exact defect
  the derivation exists to prevent. Now derived; verified by mutation that
  adding 'Collection' to it is a compile error.

- The error path fell into the min-query guidance branch, so a valid 2-character
  query that got a 500 told the user to type at least 2 characters. That branch
  conflated "nothing searched yet" with "the last search failed". Newly
  introduced by the previous commit's error-path reset; now gated on !error and
  pinned by a test.

- The aria-live region was mounted conditionally, creating the region and its
  text in one commit — which most screen readers do not announce. It is now
  mounted unconditionally with the condition inside.

- The §3b lesson mis-stated where the duplicate gate lived: it was inside
  runSearch, the genuine single sink, NOT at one of the callers — so the rule as
  written ("put the gate in the single sink, not at each caller") described the
  revision that was rejected. Reworded to the actual lesson: the gate's home is
  the shared helper, and "it's the single sink" is not evidence it is the only
  guard. That misreading is why #685 got this wrong twice.

Declined again, with reasons: the NaN pageSize edge (faithful to the sibling
helper), the clamp test's unpinned lower bound (same), and the registry's
disclosed same-identity substitution gap.

refs #740
This commit is contained in:
2026-08-05 19:20:26 +02:00
parent b6bf94f129
commit d7725c274c
4 changed files with 37 additions and 16 deletions
+7 -4
View File
@@ -187,10 +187,13 @@ const REGISTRY: RegistryEntry[] = [
"real match count below what totalCount reports, and AddItemsDialog now sums each kind's " +
"totalCount and renders a 'Showing N of M' hint when it exceeds the rendered rows. The only " +
'remaining client-side filter in AddItemsDialog (ADDABLE_TYPES.has(item.mediaType)) is inert, ' +
'not a silent drop, and this is now enforced by the type system rather than by convention: ' +
"MediaKindFilter is derived from ADDABLE_TYPE_LIST (`(typeof ADDABLE_TYPE_LIST)[number]`), so " +
'adding a non-addable kind to MEDIA_KIND_FILTERS is a compile error, not a silently-possible ' +
'mismatch.'
'not a silent drop, and this is now enforced by the type system rather than by convention on ' +
'BOTH ingress paths into the searched kinds — MediaKindFilter (the explicit-chip path) and ' +
'DEFAULT_SEARCH_KINDS (the `all` fan-out) are each derived from ADDABLE_TYPE_LIST via ' +
'`(typeof ADDABLE_TYPE_LIST)[number]`, so adding a non-addable kind to either is a compile ' +
'error. Enforcing only the first was the #685 round-3 review finding: the hint sums ' +
'PRE-filter totalCounts against POST-filter rows, so one unenforced ingress is enough to ' +
'overstate it with every row of that kind dropped.'
},
{
file: 'api/paging.ts',