Two discovered-row callsites still formatted labels inline, so a known
Databricks endpoint returned by discovery without a name rendered as its
raw ID instead of the curated registry name — the exact inconsistency the
shared resolver exists to prevent.
The generator also trusted the models.dev payload shape: a non-object
model value silently emitted `id -> id`, baking a wrong label into both
committed artifacts where no test could distinguish it from a genuine
pass-through. Validation now rejects that instead. Registry parity is
pinned across the whole table rather than five sampled rows, so a
half-committed regenerate cannot pass.
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
- Generator emits both Rust and TypeScript registries in one invocation;
both are formatted (cargo fmt, biome) so regenerate is byte-for-byte stable
- TS registry uses Map<string, string> — eliminates Object.prototype key hole
where 'constructor', '__proto__', 'toString' resolved through Object.prototype
instead of passing through as raw IDs
- Centralize three-tier resolver (resolveModelLabel) in formatAgentModelLabel.ts;
apply to ModelPicker trigger + unsupported-switching state, usePersonaModelDiscovery
default rows, and AgentConfigFields defaultModelLabel — all surfaces now show
curated names where available instead of raw endpoint IDs
- lib.rs module ordering fixed; just fmt-check green
- Tests: prototype-key cases (constructor, __proto__, toString, hasOwnProperty),
resolver precedence (discovered name wins, blank falls through), DATABRICKS_MODEL_NAMES
Map type assertion, Rust/TS parity spot-check; 3794 TS tests pass, 0 fail
- AGENTS.md refresh instructions updated: generator emits both files, resolveModelLabel
is the single frontend resolver entry point; curl --fail + --max-time 30, explicit
JSON shape validation, key/value character validation added to generator
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
The frontend heuristic in #3586 title-cased every word and converted adjacent
digit pairs to decimals for any string starting with 'databricks-'. This
invented false labels for custom workspace endpoints: 'databricks-team-2025-01'
→ 'Team 2025.01', 'databricks-finance-2025-01-30' → 'Finance 2025.01 30'.
Replace with a lookup-and-pass-through approach drawn from the same design
goose uses in production:
- Generator script (scripts/generate-databricks-model-names.py) fetches
https://models.dev/api.json and emits a sorted Rust static slice of
(id, name) pairs under crates/buzz-agent/src/databricks_model_names.rs.
Refresh by rerunning the script and committing the diff.
- catalog.rs consults the table via databricks_model_name(id): known managed
endpoints get curated names (e.g. 'databricks-gpt-5-5' → 'GPT-5.5'),
unknown/custom endpoints return their raw ID unchanged. Applied to all
ModelEntry construction paths: v1/v2 discovery, the empty-list fallback,
and discovery_failure_fallback.
- Frontend: a matching TS registry (desktop/src/features/agents/lib/
databricksModelNames.ts) covers persisted raw IDs on cards, rows, and
popovers that render before discovery data is available. formatAgentModelLabel
and formatDefaultModelLabel use the registry; no heuristic string mangling
exists anywhere in the TS layer.
- AGENTS.md documents the three-tier label precedence: API/runtime name first,
table-backed fallback second, raw ID last.
Supersedes #3586 (kennylopez-model-display-labels).
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>