fix(routers): four small edges - error shape, lock key, typeahead feedback
The low-severity residue of the adversarial review of this stack,
batched because each is a few lines and none changes a contract.
1. 42P01 ALONE DOES NOT MEAN "OUR TABLE IS MISSING"
The deploy-before-migrate tolerance in getUserRouterModels swallowed
any undefined_table error and answered "no saved models". But 42P01
is raised for whatever relation was missing - a policy, view or
trigger reaching for some other dropped table raises it from this
query too. The PGRST205 arm already required the message to name
user_router_models; the 42P01 arm now does the same, so a genuine
schema fault surfaces instead of quietly reading as an empty
selection.
2. ADVISORY LOCKS ARE KEYED WITH hashtextextended IN THIS REPO
replace_user_router_models keyed its per-(user, router) advisory lock
with hashtext(...) - an int4 hash. Every other advisory lock in this
schema uses hashtextextended(..., 0), an int8. Two lock keys collide
when their hashes collide, and collisions in a 32-bit space are not
exotic: unrelated writers would then serialize on each other for no
reason. Changed in BOTH of the places that now carry the hardened
definition - backend/migrations/20260819_01_harden_replace_user_router_
models.sql (the forward-shipping step for existing deployments) and
backend/schema.sql (the fresh install) - kept byte-identical between
them, so the function body diffs clean. The already-merged
20260818_01 is not touched.
3. ENTER THAT DOES NOTHING MUST SAY WHY
The typeahead's "Enter adds the typed id" path returned silently
whenever the text was not id-shaped. From the user's side that is
indistinguishable from a dead key. Enter on non-empty, non-id text
now sets the same error line the save failure uses; an empty box
stays silent, because nothing was asked for.
4. TWO SMALLER EDGES IN THE SAME COMPONENT
- ARIA: the "Press Enter to add this model ID" hint and the "No
matching models" placeholder lived INSIDE role="listbox", which
admits only option/group children. Both move out to the surface
around it; the listbox now holds options only, so the indices a
screen reader announces line up with aria-activedescendant.
- The 200-character model_id CHECK from the migration is mirrored
into normalizeTypedModelId, so an over-long paste fails in the box
with "Model IDs are at most 200 characters." instead of as an
opaque 400 from the profile PATCH.
Also here: the stale-selection effect added earlier in this branch trips
react-hooks/set-state-in-effect, which is an ERROR in this config - the
frontend lint gate was red on this branch. Suppressed with the reason
(it reconciles state against asynchronously arriving data, and the
functional update is a no-op unless the selection is genuinely stale).
Tests: routerModels.test.ts (42P01 naming a different relation must
reject - pre-fix "promise resolved '[]' instead of rejecting");
RouterSettingsSection.test.tsx (the no-op-Enter case becomes a
feedback case, plus an over-long-id case, a hint-outside-the-listbox
case, and a normalizeTypedModelId length boundary at exactly 200). All
five fail pre-fix.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Repository | open-legal-products/mike |
|---|---|
| Author | Amal <mamalanand3@gmail.com> |
| Authored | |
| Committed | |
| Parents | a63a615e |
| Stats | 7 files changed , +150 , -26 |
| Part of | Harden model-router selection and failure handling |
Capture this commit into my fork
Download a Markdown prompt that tells Claude how to port this
exact commit into your working tree. Run it via
claude -p < capture-commit-57cf3b11.md
from inside the repo you want the change in.