# What's new for developers (CBX 4)

> New and changed capabilities that matter when you build on, extend, or operate CBX 4 as a developer. For how to fix customization code broken by these, see t…

Source: CBX documentation, version 4.0 preview (unreleased). Canonical page: https://docs.configbox.at/docs/4.0-preview/migration-to-cb4/whats-new-for-devs. Last updated 2026-08-25.

---
New and changed capabilities that matter when you build on, extend, or operate CBX 4 as a developer.
For *how to fix* customization code broken by these, see the [breaking-changes log](https://docs.configbox.at/docs/4.0-preview/migration-to-cb4/breaking-changes-log)
and the playbooks; this page is the "what you now have" companion.

> **This page is about the jump to 4.0.** For what has been built *since* — the HTTP API, the MCP
> server, the CLI suite, generated record classes and schemas, prepared statements — see
> **[Latest features](https://docs.configbox.at/docs/4.0-preview/features/latest/)**, and
> [the HTTP API](https://docs.configbox.at/docs/4.0-preview/features/latest/http-api),
> [the MCP server](https://docs.configbox.at/docs/4.0-preview/features/latest/mcp-server) and
> [platform & extensibility](https://docs.configbox.at/docs/4.0-preview/features/latest/platform-and-extensibility) in particular. That set
> is cut by *surface* rather than by audience, so it is a different read, not a continuation of this
> one.

## Simpler answer model

- **One `#__configbox_answers` table** replaces the old `options` + `xref_element_option` pair. Every
  answer belongs to exactly one question (clean 1:n) and is a self-contained row: its price/sku/weight/
  images/custom fields and its title/description strings all hang off the answer id. No more "global option"
  reuse, no more merge-at-cache-time, no more two-ids-per-answer.
- **Why you'll like it:** answer queries are single-table; the answer id is the one identity to reason
  about; the admin has a single **Answers** editor instead of a global-options list + an assignment editor.
- **Compat shims** mean cleanly-written customizations keep working (legacy `getModel()` aliases, legacy
  `model_property_customization` loading).

**Read more:** [the answer collapse, in depth](https://docs.configbox.at/docs/4.0-preview/migration-to-cb4/answers-collapse-deep-dive) — the old and new shapes,
what the migration does to your data, and every shim · [looking up
assignments](https://docs.configbox.at/docs/4.0-preview/migration-to-cb4/assignments-lookups) · [building a product, from the
top](https://docs.configbox.at/docs/4.0-preview/functional/configurator_building)

## Reactive configurator client state (a Redux-style store)

- The configurator page's client state now lives in a small **Redux-style store** — a new AMD module
  `configbox/store` (`assets/javascript/store.js`) — instead of a write-once jQuery blob on
  `#configurator-data`. It's a single immutable state tree with `dispatch(action)` / `getState()` /
  `subscribe(listener)` / selectors, normalised `entities.questions` (by-id) + a per-question `selections`
  map, and one `MERGE_SERVER_RESPONSE` action that folds each configurator server response in atomically.
  The state is **maintained through interactions** (a selection round-trip updates
  `missingProductSelections`, `pricing`, the recorded selection, …) — the old blob drifted out of sync.
- **Backward-compatible.** The classic `configurator.getConfiguratorData()` /
  `setConfiguratorDataItem()` / `replaceConfiguratorData()` / `getQuestionPropValue()` /
  `questionHasProperty()` accessors are now thin shims over the store, and the store mirrors a flat snapshot
  back into `#configurator-data`'s jQuery `.data('json')` after every change. New code can use
  `configurator.getStore()` or `cbrequire(['configbox/store'], …)` directly.
- **Gotcha:** the DOM `#configurator-data` **`data-json` attribute** is only the frozen initial seed — read
  the store (or the accessor) for current state. And jQuery is AMD-scoped (`cbj`), not a global.

**Read more:** [the configurator client store](https://docs.configbox.at/docs/4.0-preview/technical/configurator_store) — the
state tree, every action and selector, the legacy bridge, and how customization JS should read and
watch it · [the configurator loop, server and
client](https://docs.configbox.at/docs/4.0-preview/technical/configurator_questions) · the dated store entry in the
[breaking-changes log](https://docs.configbox.at/docs/4.0-preview/migration-to-cb4/breaking-changes-log)

## A real CLI command suite (Joomla 5 console)

Run via `php cli/joomla.php configbox:<cmd>`, grouped under the `configbox` namespace:

- `configbox:cache:clear` — clear all CB caches.
- `configbox:migrate` — apply outstanding migration scripts (`--clear-failed-flag` to recover a frozen
  update; `--status` to report what is pending without applying anything; `-v` for the full failure
  diagnostics — exception chain, stack trace, migration-log tail).
- `configbox:migrate:unblock` — lift the freeze a failed migration puts on the update system. Explains
  the block first (which script, which error, whether a run is in progress), then clears it;
  `--dry-run` to look without touching, `--skip-version=<v>` to mark an unfixable script as applied
  without running it.
- `configbox:run-task <controller> [task] [key=value…] [--user …] [--output-mode …]` — run **any** CB
  controller task from the CLI exactly as the `/cb-api/` endpoint would (inject GET/POST-style params,
  impersonate a user so CB behaves as if they're logged in, capture the chrome-free output).
- `configbox:config:get|set|list` — read/write admin **settings** through `ConfigboxModelConfig`, so a
  `set` runs the **same validation a UI save runs**; handles translatables per `--language`; `list` flags
  which settings are editable.
- `configbox:sysvar:get|set|list` — the low-level `#__configbox_system_vars` bookkeeping store.

**Reusable core:** all the logic is in a platform-agnostic `ConfigboxCliHelper` (Kenedo, no Joomla), so the
same commands can be re-exposed on WordPress (WP-CLI) and Magento with thin wrappers. Registration is a
`console`-group plugin (Joomla 5 has no component console-command tag).

**Read more:** [the CLI command suite](https://docs.configbox.at/docs/4.0-preview/technical/cli_commands) — every command with
its arguments, options and exit codes, the core/wrapper split, and the WordPress and Magento
invocations · [what the suite has gained since](https://docs.configbox.at/docs/4.0-preview/features/latest/cli) · [adding your own commands
in the customization layer](https://docs.configbox.at/docs/4.0-preview/customization/custom_cli_commands)

## Admin light / dark / auto theming

- The backend supports light/dark/auto, anchored on the standard **`data-bs-theme`** attribute (Bootstrap
  5.3 colour-mode). On Joomla it inherits the Atum template's choice automatically; other platforms resolve
  it via a new **`KenedoPlatform::getAdminColorScheme()`** method (`prefers-color-scheme` fallback for auto).
- Admin colours are now **CSS custom properties** (`--cb-*`) in `assets/css/admin-theme.css` — a real token
  layer with light/dark/auto blocks and vendor (Tom Select / jQuery UI) overrides. Custom admin CSS should
  reference these tokens so it flips with the scheme.

**Read more:** [admin theming — the `--cb-*` token
layer](https://docs.configbox.at/docs/4.0-preview/technical/admin_theming) — how each platform resolves the scheme, the full
token set, the vendor chrome, and the rules custom admin CSS has to follow · [host-adaptation
stylesheets](https://docs.configbox.at/docs/4.0-preview/technical/host_stylesheets) · the dark-mode entry in the
[breaking-changes log](https://docs.configbox.at/docs/4.0-preview/migration-to-cb4/breaking-changes-log)

## Better migration ergonomics

- The whole update-script workflow is written down: the idempotency helpers
  (`tableExists`/`tableFieldExists`/`keyExists`/`getFkConstraintName`), the `failed_update_detected`
  freeze, the dedicated `configbox_upgrade_errors.log`, and the CLI debug loop.
- Migrations can now be driven explicitly from the CLI (`configbox:migrate`) instead of only implicitly on
  init — so a deploy step surfaces failures in your deploy log, not to the first visitor.
- **Pending migrations are now visible.** CBX tracks its schema version in
  `#__configbox_system_vars`, not in the host's extension registry — which is why Joomla's extension
  version and Magento's `setup:db:status` both report "up to date" while CBX migrations are
  outstanding, and the only symptom is a 500 on a product page. `configbox:migrate --status` reports the
  installed version, the newest shipped one, and every pending core/customization script, applies nothing,
  and exits non-zero when work is due — so a deploy or CI step can gate on it:

  ```
  php cli/joomla.php configbox:migrate --status
  ```

  The equivalent raw check is
  `SELECT value FROM #__configbox_system_vars WHERE \`key\` = 'latest_update_version'`, compared against
  the newest file in `helpers/updates/`.
- **Ordering to be aware of:** `applyUpdates()` runs **all core scripts, then all customization scripts**.
  Harmless on an existing site; on a *fresh* install it means core migrates to current (renaming
  `#__configbox_elements`) before your historical customization scripts run against the new schema. Write
  customization update scripts defensively, or collapse them into a current-vocabulary baseline.

**Read more:** [database migrations](https://docs.configbox.at/docs/4.0-preview/technical/migrations) — writing an idempotent
update script, both tracks, the freeze and how to lift it, and the debug loop · [the CLI
reference](https://docs.configbox.at/docs/4.0-preview/technical/cli_commands) · [what deploy tooling has gained
since](https://docs.configbox.at/docs/4.0-preview/features/latest/cli)

## Customization compatibility as a first-class concern

- The core ships **auto-conversion shims** for the answer collapse (model aliases, legacy customization-file
  loading with key remaps, graceful log-and-skip instead of white-screening).
- This **`migration-to-cb4/` docs area** is the standing home for every breaking change: a running log, a
  per-kind playbook, a deep dive, and per-audience orientation. New breaking changes get logged here as a
  rule (see the README's rules section).

**Read more:** [the customization layer, in full](https://docs.configbox.at/docs/4.0-preview/customization/customization_overview)
— every extension point, where each resolves, and which mechanism to reach for · [model-property
customizations after the collapse](https://docs.configbox.at/docs/4.0-preview/migration-to-cb4/model-property-customizations) · [what the layer can now
extend](https://docs.configbox.at/docs/4.0-preview/features/latest/platform-and-extensibility)

## Housekeeping you should know about

- Public pricing API renamed `ConfigboxPrices::getXref*()` → `getAnswer*()`; cache keys `*ByXref` →
  `*ByAnswer` and `xref_to_*` → `answer_to_question` (the answer-collapse first produced `answer_to_element`,
  then the element→question rename renamed it again; the old forms remain as aliases).

**Read more:** [the old → new identifier cheat-sheet](https://docs.configbox.at/docs/4.0-preview/migration-to-cb4/CHEATSHEET) — every renamed identifier with
its status (shim, manual, manual+data) and a grep pack to find the hits · [the breaking-changes
log](https://docs.configbox.at/docs/4.0-preview/migration-to-cb4/breaking-changes-log)

## The "element" entity is now "question" (code + DB + engine)

- The configurator entity that the UI always called a **question** was still `element` in the code, DB and
  admin MVC. CBX 4 finishes the transition: table `#__configbox_elements` → `#__configbox_questions`,
  every `*element_id*`/feature column → `question_*`, model `ConfigboxModelAdminelements` →
  `ConfigboxModelQuestions`, pricing API `getElement*()` → `getQuestion*()`, assignment/pricing cache
  keys `element_*`/`*ByElement` → `question_*`/`*ByQuestion`, the rule/calc classes
  `Configbox{Condition,CalcTerm}ElementAttribute` → `…QuestionProperty`, and the calc DSL tokens
  (`ElementAttribute(` → `QuestionProperty(`, etc.). The entity **id is unchanged**.
- The DB half ships as `helpers/updates/3.5.2.php` (applied by `configbox:migrate`); the stored-rules
  transform is **model-driven** (it discovers and converts custom rule columns too). Backward-compat shims
  cover the model class, pricing methods, cache keys and the customization-file name; the rule/calc **type
  strings and DSL tokens are a hard rename** (stored data migrated, no alias).

**Read more:** [the element → question rename, before → after](https://docs.configbox.at/docs/4.0-preview/migration-to-cb4/element-question-rename) — the full
identifier map and the per-file decision procedure · [the old → new
cheat-sheet](https://docs.configbox.at/docs/4.0-preview/migration-to-cb4/CHEATSHEET) · [the rule engine](https://docs.configbox.at/docs/4.0-preview/technical/rule_engine) and [the
calculation engine](https://docs.configbox.at/docs/4.0-preview/technical/calculation_engine), whose vocabularies changed with it
