Skip to main content
July 24, 2026 · v0.5.111 – v0.5.118

New Features

World Model Opportunity Sizing

Selecting a measure on the World Model Graph now answers “how do I improve this measure, and by how much?” — turning the semantic layer’s measure panel into a place to find, size, and explain real upside:
  • Sized opportunities - Oxygen scans a measure’s segments on selection, sizes each underperforming one against an auto-picked benchmark on a per-unit rate (gap × volume, never raw totals), and ranks the survivors as a dollar figure — e.g. “mobile orders run 218.50/orderbelowinstore+218.50/order below in-store → +264.6k.”
  • Recursive drill - Every opportunity expands into a breakdown of where the gap comes from, alternating the measure’s own component splits and dimension splits, with each child provably summing back to its parent.
  • Drivers - A measure’s drivers are ranked by how strongly they move it (elasticity), each with direction, strength, the transitive path through the tree, and lag.
  • What-if prediction - Try a hypothetical move on a single driver and see it propagated up the tree to the target, expressed in the driver’s own units.
  • Trustworthy by construction - Every sized segment must pass a statistical significance test before it appears, and a measure with no per-unit denominator is refused rather than sized — so a segment is never flagged as headroom just for being small. The analytics agent’s opportunity and impact answers in chat draw on the same corrected sizing.

Custom App Asset Storage (ctx.storage)

Oxy Functions inside a custom app now get a private, per-app file store for both uploaded and generated assets — everything from a user’s submitted attachment to a PDF or Parquet file the app produces itself lives in one isolated silo.
  • Direct browser uploads and downloads - getUploadUrl and getDownloadUrl mint presigned URLs the browser talks to storage with directly, so file bytes never round-trip through the function or Oxy — uploads and downloads stay fast even for large files.
  • First-class binary output - put writes generated files (with base64 encoding), so producing a PDF, PNG, or Parquet from a function and handing back a download link is a first-class flow, not a workaround.
  • Full file lifecycle - A complete surface — put, get, head, list, delete, and copy — with cursor-paginated listing that stays efficient on a large store. This makes patterns like “a historical list of submitted reports” straightforward to build.
  • Safe by default - Reads and writes are gated behind explicit storage: { read, write } capabilities; there is no public-access mode, so every read goes through a presigned URL. Overwrites are disabled by default and enforced atomically, so two concurrent writes can’t clobber each other.

Email Attachments from Oxy Functions

The ctx.email.send capability introduced last release now supports attachments, so a function can email a generated report or exported file, not just an HTML body.
  • Attachments on send - Pass an attachments array to ctx.email.send to deliver files alongside the message, still gated behind the app’s email: { send: true } capability.
  • Sensible limits with a clear path - Up to 20 attachments and 10 MiB total per message; when a payload is too large, the error points you toward storing the file and emailing a download link instead — pairing naturally with the new asset store.

Per-App Visibility and Roles

Custom apps can now be scoped to a subset of an org and carry their own admin role, so a sensitive internal app isn’t visible to every member and the right people can administer it.
  • App visibility - An app can be visible to the whole org (the default) or restricted to an explicit member list, so a workspace can ship an app that only its intended audience sees.
  • App-level admin - Any org owner or admin can administer the org’s apps; a per-app admin role can additionally extend admin rights downward to a non-officer — for example the warehouse admin who runs an app but isn’t org staff.
  • Trustworthy admin views - An app asks the authorization model for the current user’s role rather than inferring it, so an app’s privileged surface (such as an admin view) is gated by a verdict a client can’t forge.

One-Click Org Provisioning and Escapable Onboarding

Spinning up a new org and getting its owner set up is now a single action for both Oxy staff and partners — and workspace onboarding is no longer a place you can get stuck.
  • Create an org and onboard its owner in one step - A “New org” action in the admin Tenants cockpit (and the equivalent partner surface) creates the org and onboards its owner at once: an existing user is seeded as Owner, an unknown email receives an Owner invitation. Partners can now onboard their own clients — the create-org capability defaults on for new partnerships.
  • Onboarding always has an exit - An in-progress workspace setup no longer traps you. An org switcher in the onboarding header lets you switch orgs or create a new one, “Cancel setup” removes the half-built workspace, and returning Home defers setup (surfacing a “finish setup” row) instead of bouncing you straight back into it.

Assume an Org’s Role — From the Admin App Page and the CLI

Oxy staff and partners can now step into an org’s role with far less friction, whether previewing a custom app in the browser or working from the terminal.
  • One-click “Act as org” from the app page - The admin app-detail page now has an “Act as org” action that assumes the org’s role and lands you directly on the running app with real data, then returns you to the same page when you stop — instead of leaving to find the org, assuming, and navigating back by hand.
  • oxy assume in the CLI - oxy assume start | status | end drives the same time-boxed assume-role flow from the terminal, accepting an org slug, UUID, or URL and recording the required reason to the impersonation log. oxy login --assume <org> logs in and assumes in one step.
  • URL-aware --env - --env now accepts a pasted environment or org URL (e.g. https://poke-house.oxygen-hq.com) and resolves the target — and the org — from it, alongside every existing named value.
Both keep the existing 60-minute, non-renewable session bound; nothing about the authorization model changed.

A Standalone Custom-App Scaffold

create-oxy-app <name> now scaffolds a real, self-contained custom app instead of dropping a backend-and-CI project on you.
  • A frontend-first starter - The default template ships a Tailwind v4 design-token setup, a small chrome kit, launcher-card artwork, and example useQuery / useSemanticQuery surfaces that render real results with zero tables set up — no server functions, email templates, or CI workflows to delete first.
  • Backend is now opt-in - create-oxy-app <name> --template functions adds the server-side pieces — an Oxy Function, a preact email template, and the email.send capability — so scaffolding a backend is an explicit choice.

Workspace Health Smoke Tests

Workspace health monitoring can now actively exercise a workspace instead of only reading passive status rows — catching an expired warehouse credential, a .view.yml that no longer compiles, or an unset LLM key before a user hits the failure themselves.
  • Four probe kinds - A smoke test runs a SELECT 1 against every configured database (Connections), a compiled measure query per topic (Semantic layer), every task of a Data App (Data apps), and a fixed question put end-to-end to an agentic agent (Agent) — each producing its own health verdict.
  • Configured in config.yml - A new health_check.smoke_test block turns probes on with their own cadence. Connection checks run for free by default (a zero-scan credential check), while the data- and token-spending probes are opt-in; named topics and apps can be probed individually or swept wholesale.
  • Honest, capped verdicts - A topic or app named in a selection but missing reads as Unhealthy rather than silently skipped, a timed-out or cold-warehouse probe degrades rather than pages, and a partial sweep reports exactly how many targets it skipped instead of masquerading as full coverage.
  • Grouped admin view - The admin workspace-health tab groups checks by probe kind with a per-group pass ratio, worst-first ordering, and a “Run smoke test” button to force a run out of cadence.
Example configuration:

Anomaly Scan & Explain in the Custom App SDK

Custom apps can now trigger anomaly scans and fetch AI root-cause explanations directly from the TypeScript SDK — the same anomaly capabilities the in-product Insights Inbox already offered, now reachable from a standalone app.
  • Scan on demand - client.anomalies.scan() kicks off a monitor scan from a custom app; a long scan returns a typed pending result (with zeroed counts) so the app can poll rather than misread it as “nothing found.”
  • AI root-cause explain - client.anomalies.explain(id) returns the recursive driver breakdown for a fired anomaly, with explain(id, { refresh: true }) to bust the cache and recompute.
  • Segment-aware types - Per-segment anomalies now carry their dimension_key and filters in the SDK types, and new AnomalyFilter / ScanFailure / ExplainOptions types make the pending-and-failure paths explicit.

Timezone-Aware Monitors and Reconciliation

Anomaly monitors (.monitor.yml) and reconciliation checks (reconcile.yml) can now be pinned to a business timezone and given a freshness allowance — so a non-UTC business and a warehouse that lags a few days stop producing false anomalies and false reconciliation drift.
  • Business-timezone windows - A new timezone (IANA, e.g. America/Los_Angeles) decides which calendar day, week, or month a window snaps to, with per-monitor and per-check overrides. A local business day is now bucketed as one day instead of being split across two UTC days, so the seasonal model is fit to the shape the business actually has.
  • Freshness watermark - A new freshness wall-clock offset (e.g. 3d) tells a monitor or check to trust nothing newer than that horizon. A warehouse that lags a couple of days no longer reads its not-yet-loaded recent buckets as a catastrophic drop, and reconciliation no longer compares a period the source hasn’t finished loading.
  • Timezone-aware schedules - Monitor scans now fire on the configured timezone’s calendar, and editing timezone: takes effect on existing schedules.
  • Backward compatible - Omitting both fields reproduces the previous behavior exactly, so every existing .monitor.yml and reconcile.yml is unaffected. Note that timezone only applies to a timestamp (datetime) time dimension — an already-bucketed date column is passed through as a local calendar date.
Example configuration:

Platform Improvements

Home & Shell

  • Cleaner workspace logo - The workspace tile in the icon rail no longer paints a faint grey square behind a transparent logo; a real logo now sits on the clean rail background, while the letter fallback keeps its chip. Most visible on the HQ home page in light mode and across custom apps.

Reliability

  • Fewer false workspace-health alerts - Workspace health monitoring no longer counts its own internal evaluation runs when judging a workspace’s job liveness, so idle and demo workspaces stop reporting a misleading “unhealthy” status.
  • World Model Graph loads reliably - The World Model Graph no longer intermittently fails to load with a “failed to read workspace” error depending on which server instance handled the request; its data now routes to the instance that owns the workspace, so the graph opens consistently in production.
  • No more “workspace directory not found” toast spam - During a rolling deploy, a workspace whose files were momentarily still being restored used to flood the homepage with “workspace directory not found” errors and bounce you between pages. Oxygen now recognizes this as a transient, self-healing state, shows a brief loading spinner, and retries automatically — the workspace opens on its own once its files are back, with no error toasts.

Integrations & Webhooks

  • Incoming webhooks resolve on every instance - Webhook deliveries (such as Toast order events that feed the World Model’s live-events panel) now read their integration configuration the same way every other request does, instead of depending on a working copy that isn’t present on the stateless serving fleet. Deliveries that were being rejected in production now flow through to a correctly configured workspace.

IDE & Git

  • Git operations use your org’s credentials - Fetch, pull, and push from the Factory now reliably use the Personal Access Token configured for your org (falling back to your own registered token), instead of silently depending on whatever credentials happened to be present on the server. A workspace with a valid token configured no longer fails with a “could not read Username for github.com” prompt, and when no token is available the error now names the actual fix rather than a cryptic terminal-prompt message.

Custom App Data Queries

  • Custom-app data calls no longer fail with “origin not allowed” - Queries from a running custom app — warehouse and semantic-layer queries, plus agent and automation runs — could return a 403 for every user in production when served through the multi-instance fleet. The custom app’s public host is now carried across the internal proxy hop so these calls succeed, while genuine cross-site requests are still correctly rejected.

Semantic Layer

  • Reliable semantic query compilation - Compiling or running a semantic query no longer intermittently fails with “Topic not found” (and an empty list of available topics) depending on which server instance handled the request. Topics and views now resolve from the compiled workspace on every instance, and a workspace that genuinely hasn’t compiled yet returns a retryable response instead of a misleading modelling error that sends you to audit YAML that was never read.

Anomaly Root-Cause Explain

  • Seasonality-aware comparisons - An anomaly explanation now compares against the same phase one seasonal cycle back — derived from the monitor’s own seasonality — instead of a hardcoded offset, so a value on a Monday is compared to the prior Monday rather than to Sunday.
  • Segment-scoped explanations - For a monitor that watches a measure across segments (group_by or filters), the explanation is now scoped to the segment that actually fired — e.g. the single restaurant that dropped to zero — instead of showing chain-wide numbers.
  • Real drivers, not calendar artifacts - The decomposition now prunes time-part and row-key dimensions (day-of-week, month, *_id, *_key), so it surfaces genuine business drivers like department, cost category, or shift instead of blaming “it’s a Wednesday.”

Access & Permissions

  • Clearer staff access prompts - When Oxy staff reach a workspace that requires an explicit, time-boxed assume-role session, the app now explains what’s needed and offers to start that session instead of showing a generic “you don’t have permission” message. The same clearer message reaches the SDK and custom apps.

Custom App Publishing

  • The live build is never garbage-collected - Build cleanup now protects the currently published and draft builds by their live channel pointers, so an app that drafts many times in a row can no longer have its live bundle reaped out from under it and start returning 404s.
  • Publishes are bounded on unpack - Uploaded bundles are now size-limited on extraction (per file, total size, and file count), so a malformed or oversized archive can’t exhaust memory on the serve process and affect other apps on the same instance.
  • Atomic publish with rollback - If storing a bundle’s bytes fails mid-publish, the app’s workspace and metadata are rolled back instead of being left pointing at a different workspace while still serving the old build — closing a path where a published bundle could silently query the wrong data plane.
  • Silent publish failures now surface - A --promote publish whose function schedule failed to register now reports it via a warning on the response instead of returning success, and a malformed oxy-app.json returns a clear error naming the parse problem instead of quietly publishing a function-less app.
  • Actionable function errors - Custom-app function errors now return structured messages the SDK surfaces, so a 404 or 500 from a function tells the author what to fix instead of a bare status code.

Invitations

  • A lapsed invite no longer blocks re-inviting - An org invitation that passed its 7-day window used to permanently block that address from being re-invited while staying invisible to admins. Expired invitations are now shown in org settings (in amber, with a count) with a Resend action that supersedes the stale one, and a fresh invitation cleanly replaces a lapsed one. Admins also gain an Invitations section with revoke on the user detail pane.

Admin Tools

  • A denser, dockable app dossier - The detail panel on admin app pages can now be docked to the right, opened as a full-width bottom drawer, or popped out into its own browser window, with collapsible sections and a layout that reflows to the panel’s width — making room for full bundle paths and UUIDs that previously truncated.

Naming

  • “Custom apps” everywhere - Internal identifiers and remaining brand copy now consistently use “custom app,” finishing the rename from the older “customer app” wording. Live app URLs, subdomains, and existing configuration are unchanged.