fix(filters): operator table sent labels as wire values — every non-self-named operator was broken (user report)

The client's operator map passed the display string as BOTH the map
key and the OpMeta.key, so the menu clicks transmitted 'doesn't
contain' / 'isn't' / 'is empty' / 'at least' / … literally. The server
rightly rejected them (unknown operator → 400), the count fetch's
catch hid the counter, and flipping away from such an operator never
came back. Only self-named ops (contains, is, has, between…) worked.

- Map keys now DERIVE from meta.key (Object.fromEntries over the op
  list) — wire value and label structurally cannot diverge again.
- New scripts/filter_builder_harness.js: a playwright harness that
  drives the real drawer in headless Chromium and sweeps every author
  operator, capturing filter-count responses and console errors — the
  first UI-level regression net for the builder (the server tests
  could never see a client-side mis-key).
- Verified live: contains 8 → doesn't contain 60 → is 8 → isn't 60 →
  is empty 2 → refill 8; zero console errors across the sweep.
This commit is contained in:
John O'Keefe committed 2026-10-20 17:14:26 -04:00
1 parent 68701d6b94
commit 43987262a6
3 files changed
+123 -20

No files matched your search

+1
View File
@@ -31,6 +31,7 @@
"@types/markdown-it": "^14.2.0",
"@types/turndown": "^5.0.6",
"autoprefixer": "^10.4.27",
"playwright": "^1.64.1",
"tailwindcss": "^3.4.19",
"typescript": "^5.9.3",
"vite": "^7.3.1"