v9.0.0
Analytics single-form cutover (ADR-0021), honest chart taxonomy, canonical OS_* settings env vars, Google sign-in, and a smarter AI build experience in Console.
Released June 10, 2026. All @objectstack/* packages are published at 9.0.0. This is a major
release: the analytics authoring surface completes its cutover to the dataset
semantic layer, the chart taxonomy is trimmed to what actually renders, and
settings environment variables move to their canonical OS_* form.
TL;DR for upgraders: if your app defines dashboards, reports, or list
charts with inline queries (object + aggregate + field names), you must
migrate them to named datasets. If you configure settings via environment
variables, rename them to OS_<NAMESPACE>_<KEY>. Everything else is additive.
Breaking changes
1. Analytics single-form cutover — datasets are now required (ADR-0021)
The inline analytics author surface is removed from the dashboard-widget,
report, and list-chart schemas in @objectstack/spec. Every dashboard widget,
report, and list chart must now bind a semantic dataset and select
dimensions/measures by name. Define a metric once, reference it
everywhere. (An inline object/objectName + aggregate binding still exists
elsewhere in the spec — the React <ObjectChart> block and the page
element:number component — just not on these three schemas.)
Removed from the spec:
| Schema | Removed properties | Now required |
|---|---|---|
DashboardWidget | object, categoryField, categoryGranularity, valueField, aggregate, measures (and the WidgetMeasure type) | dataset + values |
Report | top-level and joined-block objectName, columns, groupingsDown, groupingsAcross, filter | dataset + values (rows are the dimensions; scope filtering moves to runtimeFilter) |
ListChart | xAxisField, yAxisFields, aggregation, groupByField | dataset + values |
Note:
Report.columnscame back one release later (9.1.0) with a different meaning — on both the report and a joined block it now names the dataset dimensions across amatrixreport, not raw report columns. A later release also flippedDashboardWidgetto reject any undeclared top-level key instead of silently stripping it, so a leftover inline-analytics key is now a hard parse error; the error map also names the pivotrowField/columnFieldand list-chartxAxisField/yAxisFields/aggregationspellings.
Migration:
- For each inline query, create a dataset with
defineDataset(...)declaring its dimensions and measures. See the Analytics Datasets guide. - Point the widget/report/chart at it:
dataset: "<name>", pickvalues(measure names) anddimensions/rows(dimension names). - Presentation-scope filtering goes through the widget's
filter, which is applied as the dataset'sruntimeFilter. - A flat record listing (the former
tabularreport or inline list) is not an analytics dataset — model it as an object-bound ListView (ADR-0017).
2. ChartTypeSchema trimmed to distinctly-renderable families
Eight chart types that only ever rendered as their base chart are removed, so the taxonomy no longer advertises variants the renderer doesn't draw. Update any widget or series using a removed type to its base:
| Removed type | Use instead |
|---|---|
grouped-bar, stacked-bar, bi-polar-bar | bar |
stacked-area | area |
step-line, spline | line |
pyramid | funnel |
bubble | scatter |
Kept: bar / horizontal-bar / column, line / area, pie / donut /
funnel, scatter, treemap / sankey, radar, table / pivot, and the
single-value performance family (metric / kpi / gauge / solid-gauge /
bullet). Removed variants can return behind an opt-in renderer once a real
renderer and data model back them.
Note: stacking has since come back as a property of the series rather than a chart family — the renderer now honors
ChartSeries.stack(series sharing a group id stack, otherwise they group), so a stacked bar or area today is thebar/areafamily plus a sharedstackgroup id on the series. The taxonomy above is unchanged.
3. Settings env overrides: canonical OS_<NAMESPACE>_<KEY> only
@objectstack/service-settings no longer accepts unprefixed aliases for
settings-namespace environment overrides. Rename your deployment variables:
| Before | After |
|---|---|
AI_OPENAI_BASE_URL (or ai.openai_base_url) | OS_AI_OPENAI_BASE_URL |
FEATURE_FLAGS_AI_ENABLED | OS_FEATURE_FLAGS_AI_ENABLED |
The general rule: OS_ + namespace + key, upper-snake-cased. See the
Environment Variables guide.
4. @object-ui/plugin-workflow removed
The package authored BPMN-style workflow shapes the ObjectStack automation
engine does not execute (ADR-0020, ADR-0031). If you imported it in a custom
UI, the replacements ship already: flow authoring is the metadata-admin
FlowCanvas (engine-registry-driven palette) in @object-ui/app-shell, run
history is FlowRunsPanel, and approval UI comes from the framework's
@objectstack/plugin-approvals.
New in the framework
Analytics results are now presentation-ready
Three @objectstack/service-analytics improvements make charts and reports
human-readable with no frontend work:
- Dimension display labels. A
selectdimension shows its option label (Backlog, notbacklog); alookup/master_detaildimension shows the related record's display name instead of its foreign-key id. Resolution happens server-side inqueryDataset. - Date dimensions bucket and format by granularity. A monthly trend chart
now yields one point per month with sort-stable labels (
2026,2026-Q2,2026-04) — no more raw epoch milliseconds on the axis. - Measure label and format travel with the result.
AnalyticsResult.fields[]gains optionallabelandformat, so a chart can display "Tasks" and "$616,000" while keeping raw numbers for plotting.
Automation: durable pause survives subflows and restarts
Two reliability upgrades to @objectstack/service-automation's durable pause
(ADR-0019):
- Nested durable pause. A pausing node (approval, screen, wait) inside a subflow used to fail the parent run. The whole chain now suspends as linked runs: the child's screen surfaces through the parent, resume signals delegate down to the suspended child, and a completed child auto-resumes its parent.
- Wait timers re-arm after a cold boot. A timer
waitscheduled its wake-up in process memory, so a restart left the suspended run paused forever. The wake deadline is now persisted with the run; on boot the service resumes overdue runs immediately and re-schedules future ones.
Publishing: seed data lands on every publish path
Whether a seed draft's rows actually materialized used to depend on which
route you published through — the per-ref publish (used by the Studio home
banner) never applied them, yielding "Published!" with empty tables. Seed
application now lives in the metadata protocol itself (today
@objectstack/metadata-protocol), so every publish path converges on it and
reports the outcome under seedApplied. A seed problem never fails the
publish — callers must check seedApplied.success instead of assuming the data
went live.
AI service: tools stream progress while they run
ToolExecutionContext.onProgress?.(part) lets a long-running tool report
progress before it returns, emitted as custom data-* parts on the UI message
stream (this is what powers the live build tree in Console's Build-with-AI).
It is set only on the streaming path (streamChatWithTools) and is undefined
for non-streaming/system calls, so tools must call it optionally. Existing
tools that never emit behave exactly as before.
Auth: Google sign-in and an extension hook
@objectstack/plugin-auth binds the auth settings namespace to better-auth
runtime configuration and exposes an extension hook for provider packages.
Google sign-in works out of the box — configure it in Setup →
Authentication or via deployment-level GOOGLE_CLIENT_ID /
GOOGLE_CLIENT_SECRET. See Auth & SSO.
CLI: AI provider SDKs bundled
The CLI ships @ai-sdk/openai, @ai-sdk/anthropic, and @ai-sdk/google as
direct dependencies. These were previously only optional peer dependencies of
@objectstack/service-ai, so a globally-installed CLI couldn't resolve them at
runtime — configuring an OpenAI-compatible provider (DeepSeek, DashScope,
SiliconFlow, OpenRouter, Cloudflare, which all normalize to provider=openai
and dynamically import @ai-sdk/openai) failed with "Could not build adapter
for provider=…". Bundling them makes those providers work out of the box.
Note: later releases moved the provider SDKs back to a host-install model — in the current CLI they are not bundled. The
aisettings namespace states that provider SDK packages (e.g.@ai-sdk/openai) must be installed on the host for the chosen provider to load, and the AI runtime runs through the host-declared@objectstack/service-aiplugin, which auto-detects the provider from environment variables (AI_GATEWAY_MODEL,OPENAI_API_KEY, …).
New in Console (Studio)
The Console build frozen into @objectstack/console 9.0.0 picks up the
dataset cutover end-to-end, plus a round of AI-builder and designer
improvements:
- Dataset widgets render as their true chart type — pie, donut, funnel, line, area, scatter, radar, treemap, sankey, table, and pivot all route to their real renderer, and metric cards display the measure's label and format.
- Reports render the dataset form (ADR-0021), including matrix and joined variants; widget filters apply as dataset runtime filters.
- Flow designer aligned with the engine vocabulary, with a runs panel for inspecting executions; toolbar flow/script actions now resolve the selected record correctly.
- Build-with-AI: a live build tree streams progress while the agent scaffolds an app, finished apps can auto-publish in chat, seed-data results are surfaced, and the consumer panel no longer dumps raw tool JSON.
- Marketplace: when cloud-bound, the Installed view shows what Cloud has installed into the environment (see the Cloud section below).
- Forms: conditional rules in inline grids can now reference parent-record
fields (
parent.<field>scope). - Pages:
PageViewgains a console action runtime, so page-embedded actions behave like their grid/detail counterparts. - Charts: crowded bar-chart x-axis labels angle and truncate instead of overlapping.
ObjectStack Cloud: package publish & install
ObjectStack Cloud versions independently of this release train and deploys continuously; developer-facing changes are summarized here when a framework release lands.
The deployment story for cloud-bound apps is now publish, then install:
os package publish puts a package version in the cloud catalog, and you
install it into an environment from that environment's Marketplace in
Console — installing is a separate, explicit step rather than something a
publish does on its own (a publish installs only when you opt in with
os package publish --env <id> --install). The environment's Installed
view reflects what Cloud has installed, so packages installed via the CLI show
up there too.
Coming in the next framework release: published packages default to organization visibility, so they appear in your own environments' Marketplace immediately, with private visibility reserved for targeted distribution.
Note: this has since shipped —
os package publish --visibilityacceptsprivate/org/marketplaceand defaults toorg("auto-visible / installable across your organization's environments"). See the Publish & preview guide.
Notable fixes
- Publishing pending drafts by ref so package-less drafts can't get stuck in the metadata-admin publish queue.
- New projects scaffolded by
create-objectstackstart cleanly under pnpm. - Setup → System Settings menu labels and pages are fully translated.
FlowRunnercloses cleanly when a terminal resume fails instead of leaving a dead dialog.- The AI service treats a stored or env-locked
provider=memoryas an explicit override, while manifest defaults keep boot-time provider auto-detection intact.
Upgrade checklist
- Update all
@objectstack/*dependencies to^9.0.0(they are version-locked — upgrade them together). - Migrate inline dashboard widgets, reports, and list charts to named datasets (guide).
- Replace removed chart types with their base family (table above).
- Rename settings env vars to the
OS_<NAMESPACE>_<KEY>form. - Re-run your app's spec validation (
os validate) — it will flag any metadata still using removed properties.