Engine and app must resolve models from the same live catalog.
Implemented — Apps commit 1516f330 (local main, not pushed)
resolveCwcModelRoute already accepted an overlay; only the account API ever passed one. Every other consumer resolved against the compiled floor, so /v1/models could advertise a route that a chat turn, hosted Work launch or the Work model gateway then refused with model_not_supported.
One overlay, every consumer. services/control-plane/src/model-frontier/catalog-overlay.mjs is now the single server-side reader, memoized per store. Threaded through /api/model-providers, resolveCatalogModelRoute (chat turns, follow-up turns, hosted and local Work launch), the Work model gateway (mint / verify / invoke), sandbox bootstrap broker references, managed inference rate cards, the GitHub mention launch, and the credential use-contexts in both stores. account-api/model-api.mjs lost its private copy.
Compiled floor stopped narrowing the Runtime export. Eight providers dropped models their generated Route Contract export lists (20+ models, including anthropic/claude-haiku-4-5, openai/gpt-5.5, moonshot/kimi-k2.6, openrouter/z-ai/glm-5.3-flash, zai/GLM-5.3-Flash). The floor is now the union, and the export owns the spelling: an override id differing only by case (glm-5.3 vs GLM-5.3) is a resolvable alias reported as aliasOf. The reviewed frontier still decides what is a chat model, so image/speech/vision-only registry rows stay metadata-only.
Server data, not client releases. Admission accepts editable server_catalog_record rows for providers with no discoverable listing and for explicitly configured models on compatible endpoints; authored records do not decay on the discovery clock. Retirement is server data and withdraws floor rows, refusing with model_retired plus reason and successor. /api/model-providers gained ?q=, ?limit=/?cursor= and a catalog freshness block.
Superseded lists removed. The edge worker answers in the same shape as the Node control plane, which let the web client drop displayRuntimeCatalogProviders and its compiled copy of the runtime catalog.
Evidence
- Acceptance suite
services/control-plane/test/model-catalog-server-driven.test.mjs (10 tests): four routes present in no compiled list — a direct provider, a gateway slash/variant id, a plan-scoped record, a compatible endpoint — added through server data alone, then listed, searched, paginated, selected, saved, reloaded and executed, asserting the provider is asked for the exact upstream id. Plus retirement, stale-vs-authored evidence, offline degradation, and a guard that every server-side resolveCwcModelRoute call is handed the catalog.
- Gates:
npm test 3293 passed / 0 failed / 90 skipped; npm run check:web passed (1141 tests, lint, typecheck, build); edge worker npm run check 231 passed / 0 failed.
Still open (distinct levels of evidence)
- No provider calls were made. Real advertised models still need separate authorized qualification.
- Installed desktop and Core acceptance not exercised; GUI control remains paused.
- Not pushed, not deployed.
- Rollout constraint: the server must ship before or with its clients — there is no longer a client-side fallback for a server returning the narrow catalog.
Engine and app must resolve models from the same live catalog.
Implemented — Apps commit
1516f330(localmain, not pushed)resolveCwcModelRoutealready accepted anoverlay; only the account API ever passed one. Every other consumer resolved against the compiled floor, so/v1/modelscould advertise a route that a chat turn, hosted Work launch or the Work model gateway then refused withmodel_not_supported.One overlay, every consumer.
services/control-plane/src/model-frontier/catalog-overlay.mjsis now the single server-side reader, memoized per store. Threaded through/api/model-providers,resolveCatalogModelRoute(chat turns, follow-up turns, hosted and local Work launch), the Work model gateway (mint / verify / invoke), sandbox bootstrap broker references, managed inference rate cards, the GitHub mention launch, and the credential use-contexts in both stores.account-api/model-api.mjslost its private copy.Compiled floor stopped narrowing the Runtime export. Eight providers dropped models their generated Route Contract export lists (20+ models, including
anthropic/claude-haiku-4-5,openai/gpt-5.5,moonshot/kimi-k2.6,openrouter/z-ai/glm-5.3-flash,zai/GLM-5.3-Flash). The floor is now the union, and the export owns the spelling: an override id differing only by case (glm-5.3vsGLM-5.3) is a resolvable alias reported asaliasOf. The reviewed frontier still decides what is a chat model, so image/speech/vision-only registry rows stay metadata-only.Server data, not client releases. Admission accepts editable
server_catalog_recordrows for providers with no discoverable listing and for explicitly configured models on compatible endpoints; authored records do not decay on the discovery clock. Retirement is server data and withdraws floor rows, refusing withmodel_retiredplus reason and successor./api/model-providersgained?q=,?limit=/?cursor=and acatalogfreshness block.Superseded lists removed. The edge worker answers in the same shape as the Node control plane, which let the web client drop
displayRuntimeCatalogProvidersand its compiled copy of the runtime catalog.Evidence
services/control-plane/test/model-catalog-server-driven.test.mjs(10 tests): four routes present in no compiled list — a direct provider, a gateway slash/variant id, a plan-scoped record, a compatible endpoint — added through server data alone, then listed, searched, paginated, selected, saved, reloaded and executed, asserting the provider is asked for the exact upstream id. Plus retirement, stale-vs-authored evidence, offline degradation, and a guard that every server-sideresolveCwcModelRoutecall is handed the catalog.npm test3293 passed / 0 failed / 90 skipped;npm run check:webpassed (1141 tests, lint, typecheck, build); edge workernpm run check231 passed / 0 failed.Still open (distinct levels of evidence)