fix(filter-builder): one operator name per field (Option A); drawer ids resolve on refresh

The is/in question, settled per user decision: they were exact
synonyms (feed's clause compiles is/is_not and in/not_in to the same
ANY), so the vocabulary collapses instead of being explained.

- Registry: feed's offered ops are in/isn't-in (+has/missing);
  legacyFieldOps keeps is/is_not ACCEPTED at parse forever — every
  stored tree and saved filter from before the collapse parses and
  evaluates unchanged. Library keeps is/isn't (its only names);
  collection's genuinely distinct operators are untouched.
- Client: id-kind cells are uniform — every operator on an id field
  accepts a set, so all id cells say "pick one or more" and accumulate
  picks. The per-operator placeholder/pick branching is gone (it also
  never fired: the registry types is as Values:"many").
- The drawer-refresh id fix rides along: hydrateFromForm now calls
  resolveIdLabels, so a page refresh with an active saved filter
  resolves stored ids to names in the bookshelf drawer (the collection
  rules page already did at init).

Pins: feed payload omits the synonyms while legacy feed is/is_not
trees parse (Go unit); uniform accumulate + placeholder (vitest);
menu-open regression and hydrate-resolution pins kept. Web 183/183;
services, filter + episode-tree + search/collections contract slices
green (full suite skipped per user — vocabulary + UI-layer change,
engine paths pinned by the targeted slices).
This commit is contained in:
John O'Keefe committed 2026-11-01 01:16:20 -04:00
1 parent 81e54f89b2
commit c448fa438f
5 files changed
+88 -14

No files matched your search

+16
View File
@@ -603,3 +603,19 @@ evaluation through the dashboard could never work (the test harness
wired it correctly — the gap was production-only). The dashboard
handler and both inline sites now receive it, and evaluation failures
log instead of silently emptying a section.
## 2026-11-01 — feed operator vocabulary collapsed (Option A): one name per semantic
The feed field's menu offered `is`/`isn't` alongside `in`/`isn't in` —
exact synonyms (the clause compiles both pairs to the same `ANY`). The
MENUS now offer `in`/`isn't in` (+ has/missing) only. This is an
offered-vocabulary narrowing, not a parse change:
- Stored trees and saved filters carrying `feed is X` / `feed is_not X`
PARSE AND EVALUATE FOREVER (`legacyFieldOps` accepts the synonyms at
validation — the stored wire is never invalidated).
- The fields payload (the client's operator menus) no longer carries
the duplicates.
- Client id-cells are uniform: every operator on an id-valued field
accepts a set, so all id cells say "pick one or more" and accumulate
picks — no per-operator placeholder/pick distinction to explain.