/catalogStates are contract, not decoration. A catalog that only draws the populated case produces a UI that only handles the populated case. Five states below; each is a distinct thing the API can hand us, and each tells the developer what to do next.
Skeletons preserve the populated layout so nothing jumps on
arrival. aria-busy="true" on the results region; no spinner-only screen.
The catalog is built from specs that repos publish on merge. Nothing has arrived yet — this is a cold start, not a failure.
Distinct from (c): this is zero specs in the registry. Copy points at the one-step CI action that fixes it, rather than leaving a dead end.
22 specs are in the catalog — your filters exclude all of them.
The catalog could not be refreshed
The last harvest run failed at 08:12 UTC
(502 from 3 of 22 sources). You are seeing the last known-good catalog from
2026-09-07 22:40 UTC — it may be out of date, and newly published specs will be
missing.
Total failure (no cache at all) renders the same callout with
the list replaced by [data-empty='catalog-unavailable'] and no stale rows.
Your account doesn't have developer access yet
Reading the catalog needs DevPortalCatalog:read, granted by the
Developer role on the platform root organization. Provisioning normally happens on first
sign-in; if it hasn't completed, or the capability is still flag-gated for your account, it lands here.
We do not silently show a partial catalog — this fails closed.
Test hooks: [data-frame='04-catalog-states'] · [data-state='loading'] ·
[data-state='empty'] · [data-empty='no-specs'] ·
[data-state='empty-filtered'] · [data-empty='no-results'] ·
[data-state='error'] · [data-error='harvest-failed'] ·
[data-catalog='stale'] · [data-state='forbidden'] · [data-http='403'] ·
[data-action='retry'] · [data-action='clear-filters'].