The profiles page could tell you a profile admitted 40 models and not which 40. The only surface for the list was a title= tooltip truncated to eight names, even though /admin/api/profiles returns the whole array. The detail pane now renders it. The allowed_model_ids control had a worse version of the same problem. It was one <select multiple> holding the entire 446-row catalog and showing six rows, so a profile pinned to kimi-k3 put its first selected option at index 72 and the browser's scroll restore landed several hundred pixels short of it. The selection was correct and invisible, which reads as no selection at all -- and Duplicate did not scroll at all, so nothing looked selected there under any circumstances. It is now two panes, Available and Selected. What is chosen is the only thing in the Selected pane, so it cannot go off screen. Filtering narrows Available while Selected stays put, and "Add all" takes the whole filtered set, so "filter to kimi, add all shown" is one gesture instead of a ctrl-click hunt. That also closes a silent data loss. Selection used to be read back out of selectedOptions, so a stored id with no matching <option> -- a model deprecated, renamed, or pruned from the provider allowlist since the profile was written -- was simply not selected, and the next save posted the selection without it. Opening Edit and pressing Save with no changes turned a profile pinned to one model into one that admits everything, with no warning. Selection is now a Set on the picker root, so an id the catalog no longer offers stays in Selected flagged "not routable" and round-trips. GET /admin/api/models defaults to routable rows only, because every model *picker* in the portal reads it and offering a row the router will never choose is worse than offering nothing. For an allowlisted provider that filter IS the allowlist: the poller deprecates every OpenRouter row not on provider_model_allowlist, so the picker offers exactly the 30 allowlisted models rather than all 422. A model becomes selectable by being allowlisted and then polled, which is the order an operator expects. include_unroutable=true returns everything for models.html, whose whole job is the availability table and whose "Show deprecated / stale" toggle is the intended way to see them. Also: the list summary reads "2 models" instead of "1 filter", and the helper prose under both pickers is gone -- the pane headers and the "none = all allowed" empty state say it where it is needed. Findings and what is still open: plans/admin-portal-functional-sweep.md Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VRQXz5SYZYVWscxS1QqF6U
19 KiB
19 KiB