CRITICAL: Fix Rust gpt5-base short-version guard to mirror TS regex semantics.
The old 4-char window check diverged from TS /^\d{1,3}(?:[^a-z\d]|$)/i for
inputs like gpt-5-10-preview (10 digits). Replaced with find(non-digit) approach
that exactly matches the TS digit-run semantics. Add left-boundary guards (start
or preceded by '-'/'.') to both gpt5_token_matches_rs and gpt5_base_matches_rs
to prevent sgpt-model-class false-positives.
IMPORTANT: Add exact_records validation to generator — enum checks for all
optional override axes, non-empty override check, canonical-order enforcement,
default_effort-in-override check, match_priority non-negative-int check
(injected into source comments, treated as injection surface). Add 9 schema-
negative tests in test-manifest-validator.mjs.
IMPORTANT: Restrict DBv2 gpt segment rule from starts_with("gpt") to exact
segment match ("gpt" or "gpt5"). Raise priority 5->6 to restore old dual-marker
OpenAI-before-Claude contract. Add left-boundary guards to token helpers. Add
collision-negative corpus vectors: gptoss-model, gptj-6b, gpt-neox,
customgpt-5-5-endpoint.
MINOR: Restore .trim() on model string before resolveModelCapabilities call in
buzzAgentConfig.ts. Make exact-record lookup case-insensitive (lowercase keys at
build + lookup). Extend run-corpus.mjs to compare all 6 axes including
normalization_policy and registry_label. Add positive corpus vectors: opus-5
rule, dbv2 gpt-segment, sol/luna/terra, gpt-5-4-nano exact record, openrouter/
unknown/legacy-databricks fallbacks, case-insensitive lookup vectors. Fix Rust
corpus test harness provider lowercasing to handle vectors like provider=OpenAI.
HYGIENE: Fix "Generated 3 files" -> "Generated 2 files" message. Remove dead
$schema pointer from manifest. Add module doc to generated_model_capabilities_tests.rs
noting hand-maintained status. Switch PROVIDER_ALIASES from plain object to Map
in formatAgentModelLabel.ts and run-corpus.mjs. Align CI node-version to 24.
Tighten python SAFE_NAME_RE from * to + to reject empty display names.
luna/terra: models.dev confirms databricks-gpt-5-6-luna and -terra advertise
[low,medium,high] (differs from sol's [low,medium,high,max]). Added two exact
records with supported_efforts=[low,medium,high], default_effort=medium.
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
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>