Filed on the maintainer's ruling (2026-08-31, hotcrm#1212 adjudication, verbatim: 「1212 也是」 — following 「#1203 …这种也是平台的问题啊」: this class is the platform's to fix uniformly, apps must not compensate widget-by-widget).
The measured scenario (hotcrm#1212, closed with a pointer here)
A fresh install of the flagship demo renders the sales dashboard with eleven populated tiles and one blank one — the quota-attainment table reads exactly 「暂无数据行」, sits mid-page, and reads as "the dashboard failed to load". The cause is legitimate (its data source crm_forecast has a single scheduled producer that has not run yet — by ruling hotcrm#702), the widget declaration is legitimate, and the only app-side remedy was authoring per-widget emptyState prose — the per-app tax the maintainer has ruled against (objectstack#13848 principles: apps are simplified business implementations; uniform behavior belongs to the platform).
Deliverable
Make the widget DEFAULT empty state self-explaining, uniformly:
- distinguishable from a load failure at a glance (visual treatment + wording — an empty result is a state, not an error);
- self-describing without authored copy: name what is empty (the widget's label and/or its data source) rather than the bare row-placeholder, e.g. a "no data yet in [source]" shape — exact copy/design is implementation + review judgment;
- the EXISTING authored
emptyState contract always overrides the default — this card adds no new authoring obligation and no new spec key (if implementation finds a spec-side key is genuinely needed, stop and report — that is a contract question, not a rider); - i18n via the platform's locale bundles, not per-app strings.
Acceptance scenario
hotcrm sales_dashboard fresh install (pnpm dev, no snapshot yet): the quota-attainment tile must read as an explained empty state, with zero hotcrm-side changes.
Refs: hotcrm#1212 (measurement + the corrected cause chain) · hotcrm#702 (single-producer ruling that makes seeding the window a non-option) · objectstack#13848 (application-repo principles).
Filed on the maintainer's ruling (2026-08-31, hotcrm#1212 adjudication, verbatim: 「1212 也是」 — following 「#1203 …这种也是平台的问题啊」: this class is the platform's to fix uniformly, apps must not compensate widget-by-widget).
The measured scenario (hotcrm#1212, closed with a pointer here)
A fresh install of the flagship demo renders the sales dashboard with eleven populated tiles and one blank one — the quota-attainment table reads exactly 「暂无数据行」, sits mid-page, and reads as "the dashboard failed to load". The cause is legitimate (its data source
crm_forecasthas a single scheduled producer that has not run yet — by ruling hotcrm#702), the widget declaration is legitimate, and the only app-side remedy was authoring per-widgetemptyStateprose — the per-app tax the maintainer has ruled against (objectstack#13848 principles: apps are simplified business implementations; uniform behavior belongs to the platform).Deliverable
Make the widget DEFAULT empty state self-explaining, uniformly:
emptyStatecontract always overrides the default — this card adds no new authoring obligation and no new spec key (if implementation finds a spec-side key is genuinely needed, stop and report — that is a contract question, not a rider);Acceptance scenario
hotcrm
sales_dashboardfresh install (pnpm dev, no snapshot yet): the quota-attainment tile must read as an explained empty state, with zero hotcrm-side changes.Refs: hotcrm#1212 (measurement + the corrected cause chain) · hotcrm#702 (single-producer ruling that makes seeding the window a non-option) · objectstack#13848 (application-repo principles).