From 4ea0a6090d2a258b2461059d4e57af6580bff641 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 16 Jul 2026 13:50:49 +0000 Subject: [PATCH 1/3] chore: bump objectui to 23d65c396b8c fix(i18n): drop try/catch-around-hook in createSafeTranslation / useSafeTranslate (#2605) objectui@23d65c396b8cd7f4b2a461b187a40e7b384355cd --- .changeset/console-23d65c396b8c.md | 27 +++++++++++++++++++++++++++ .objectui-sha | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 .changeset/console-23d65c396b8c.md diff --git a/.changeset/console-23d65c396b8c.md b/.changeset/console-23d65c396b8c.md new file mode 100644 index 000000000..250703f3e --- /dev/null +++ b/.changeset/console-23d65c396b8c.md @@ -0,0 +1,27 @@ +--- +"@objectstack/console": minor +--- + +Console (objectui) refreshed to `23d65c396b8c`. Frontend changes in this range: + +- fix(i18n): drop try/catch-around-hook in createSafeTranslation / useSafeTranslate (#2605) +- fix(app-shell): Studio Access matrix — history opens in-place sheet, breadcrumb stops escaping the pillar (#2599) +- fix(data-objectstack): emit mutation events from batchTransaction/bulk so related lists refresh after master-detail saves (#2607) +- fix(metadata-admin): follow the live app locale, not just navigator.language (#2602) +- feat(detail+fields+components+app-shell): record inline-edit polish (#2572) (#2604) +- fix(app-shell+kanban+list): row-predicate CEL authoring advertises runtime-bound roots; kanban binds host scope (#2571 follow-up) (#2603) +- fix(plugin-list): spec bare-string sort form crashed ListView (#2578 shape-mismatch audit) (#2601) +- fix(app-shell): lock the Access pillar permission matrix in read-only packages (#2570) +- fix(fields): localize relative-date humanize via Intl.RelativeTimeFormat (framework#3040) (#2593) +- fix(components): pin sticky leading cells at measured header widths (#2592) +- fix(app-shell,core): keep error-envelope objects out of toast.error — React #31 page crash (#2579) (#2580) +- feat(flow-designer): pick the target node per branch in the Decision Branches editor (#1942) (#2568) +- fix(core+data-objectstack+app-shell): canonicalize reference/reference_to at the schema chokepoints (#2407) (#2598) +- fix(dashboard-filters): spec-form filter options crashed the dashboard; add guide screenshots (#2578) (#2597) +- fix(fields): PeoplePicker cursor resets only on real result changes (de-flakes keyboard test) (#2594) +- fix(studio): stop force-opening the new-object dialog on empty packages (#2569) +- feat(studio): CEL editor with validate + autocomplete for field conditional rules (#1582) (#2571) +- feat(kanban): default lane field honours the ADR-0085 stageField role (#2596) +- fix(fields+detail): resolve pre-existing rules-of-hooks violations in cell renderers (#2595) + +objectui range: `fb35e4828fdb...23d65c396b8c` diff --git a/.objectui-sha b/.objectui-sha index 29df0f044..a29ad0aa9 100644 --- a/.objectui-sha +++ b/.objectui-sha @@ -1 +1 @@ -fb35e4828fdb3b6ecc23761a82e9ec4a13890727 +23d65c396b8cd7f4b2a461b187a40e7b384355cd From d79383cdc1074e421b7e0ec66a28f1f2ac14fa60 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 16 Jul 2026 13:51:58 +0000 Subject: [PATCH 2/3] feat(showcase): dogfood ADR-0085 stageField-driven default kanban lanes (#2596) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two `board` views that deliberately omit the (spec-optional) kanban binding, so the lane field resolves through the shared stageField detector: - showcase_semantic_zoo (stageField:'status') → lanes Draft/Active/Done with zero view-level config — the objectui#2596 role-driven default; - showcase_semantic_zoo_legacy (stageField:false) → NO default lanes; the pre-#2596 hard-coded 'status' fallback would have grouped by Red/Green. detail-shapes e2e gains the paired assertion (lanes present / lanes suppressed, no page errors on either board). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_016wpaFMPowSn4nAWsjeGZib --- .../app-showcase/e2e/detail-shapes.spec.ts | 21 ++++++++++ examples/app-showcase/objectstack.config.ts | 4 +- examples/app-showcase/src/ui/views/index.ts | 1 + .../src/ui/views/semantic-zoo.view.ts | 38 +++++++++++++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 examples/app-showcase/src/ui/views/semantic-zoo.view.ts diff --git a/examples/app-showcase/e2e/detail-shapes.spec.ts b/examples/app-showcase/e2e/detail-shapes.spec.ts index 9bfabe658..3ad7610ed 100644 --- a/examples/app-showcase/e2e/detail-shapes.spec.ts +++ b/examples/app-showcase/e2e/detail-shapes.spec.ts @@ -137,6 +137,27 @@ test('stageField:false: status renders as a plain field, no stepper', async ({ p expect(errors, 'uncaught page errors on stageField:false detail').toEqual([]); }); +test('kanban default lanes honour stageField — role drives lanes, strict-false suppresses them', async ({ page }) => { + // Both `board` views deliberately carry NO kanban binding (spec-optional), + // so the lane field resolves through the shared stageField detector + // (objectui#2596). + + // Positive: zoo declares stageField:'status' → lanes are the option + // labels, with zero view-level config. + let errors = await openRecord(page, `/_console/apps/${APP}/showcase_semantic_zoo?view=board`); + await expect(page.getByText('Draft', { exact: true }).first()).toBeVisible(); + await expect(page.getByText('Done', { exact: true }).first()).toBeVisible(); + expect(errors, 'uncaught page errors on stageField board').toEqual([]); + + // Negative: legacy zoo declares stageField:false (its status is a color, + // not a lifecycle) → NO default lanes. The pre-#2596 hard-coded 'status' + // fallback would have grouped by Red / Green. + errors = await openRecord(page, `/_console/apps/${APP}/showcase_semantic_zoo_legacy?view=board`); + await expect(page.getByText('Red', { exact: true })).toHaveCount(0); + await expect(page.getByText('Green', { exact: true })).toHaveCount(0); + expect(errors, 'uncaught page errors on suppressed board').toEqual([]); +}); + test('ungrouped + related-heavy: flat details and related-list tabs on Contoso', async ({ page }) => { const errors = await openRecord(page, recordUrl('showcase_account', contosoId)); diff --git a/examples/app-showcase/objectstack.config.ts b/examples/app-showcase/objectstack.config.ts index 0d9b1f6eb..3ce1ea4fa 100644 --- a/examples/app-showcase/objectstack.config.ts +++ b/examples/app-showcase/objectstack.config.ts @@ -18,7 +18,7 @@ import { ExternalCustomer, ExternalOrder } from './src/data/objects/external/ind import { setupShowcaseExternalDatasource } from './src/system/datasources/external-fixture.js'; import { registerRecalcEndpoint } from './src/system/server/recalc-endpoint.js'; import { registerShowcasePositionBindings } from './src/security/bind-position-sets.js'; -import { TaskViews, ProjectViews, InquiryViews, BusinessUnitViews } from './src/ui/views/index.js'; +import { TaskViews, ProjectViews, InquiryViews, BusinessUnitViews, SemanticZooViews, SemanticZooLegacyViews } from './src/ui/views/index.js'; import { ShowcaseApp } from './src/ui/apps/index.js'; import { ChartGalleryDashboard, OpsDashboard, RevenuePulseDashboard } from './src/ui/dashboards/index.js'; import { ShowcaseTaskDataset, ShowcaseProjectDataset, ShowcaseInvoiceDataset, ShowcaseAccountDataset } from './src/ui/datasets/index.js'; @@ -181,7 +181,7 @@ export default defineStack({ // UI apps: [ShowcaseApp], portals: allPortals, - views: [TaskViews, ProjectViews, InquiryViews, BusinessUnitViews], + views: [TaskViews, ProjectViews, InquiryViews, BusinessUnitViews, SemanticZooViews, SemanticZooLegacyViews], pages: [CapabilityMapPage, StartHerePage, ComponentGalleryPage, ProjectWorkspacePage, ProjectDetailPage, TaskWorkbenchPage, TaskTriagePage, TaskBoardPage, TaskCalendarPage, TaskGalleryPage, TaskSchedulePage, TaskTimelinePage, TaskMapPage, TaskAllViewsPage, ActiveProjectsPage, TaskDetailPage, ReviewQueuePage, NewProjectWizardPage, MyWorkPage, SettingsPage, StylingGalleryPage, CommandCenterPage, CommandCenterJsxPage, CrmWorkbenchPage, TaskDeskPage, PageVariablesPage, ContactFormPage, RenewalsPipelinePage], dashboards: [ChartGalleryDashboard, OpsDashboard, RevenuePulseDashboard], books: allBooks, diff --git a/examples/app-showcase/src/ui/views/index.ts b/examples/app-showcase/src/ui/views/index.ts index ef8f99f0f..c20d426ab 100644 --- a/examples/app-showcase/src/ui/views/index.ts +++ b/examples/app-showcase/src/ui/views/index.ts @@ -5,3 +5,4 @@ export { ProjectViews } from './project.view.js'; export { InquiryViews } from './inquiry.view.js'; export { BusinessUnitViews } from './business-unit.view.js'; export { ContactViews } from './contact.view.js'; +export { SemanticZooViews, SemanticZooLegacyViews } from './semantic-zoo.view.js'; diff --git a/examples/app-showcase/src/ui/views/semantic-zoo.view.ts b/examples/app-showcase/src/ui/views/semantic-zoo.view.ts new file mode 100644 index 000000000..5590081b7 --- /dev/null +++ b/examples/app-showcase/src/ui/views/semantic-zoo.view.ts @@ -0,0 +1,38 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +import { defineView } from '@objectstack/spec'; + +/** + * Semantic-zoo kanban views — runtime dogfood for the ADR-0085 `stageField` + * role driving DEFAULT kanban lanes (objectui#2596). + * + * Both boards deliberately omit the `kanban` binding (it is optional on the + * view schema): with no explicit `groupByField`, the lane field resolves + * through the shared `detectStatusField` semantics — + * + * - `showcase_semantic_zoo` declares `stageField: 'status'` → the board + * lanes by Draft / Active / Done with zero view-level config; + * - `showcase_semantic_zoo_legacy` declares `stageField: false` (its + * `status` is a color, not a lifecycle) → NO default lanes: the board + * renders its empty state instead of grouping by Red / Green, which is + * exactly what the pre-#2596 hard-coded 'status' fallback used to do. + * + * Guarded by `examples/app-showcase/e2e/detail-shapes.spec.ts`. + */ +export const SemanticZooViews = defineView({ + board: { + label: 'Board (lanes from stageField)', + type: 'kanban', + data: { provider: 'object' as const, object: 'showcase_semantic_zoo' }, + columns: ['name', 'code', 'amount'], + }, +}); + +export const SemanticZooLegacyViews = defineView({ + board: { + label: 'Board (no lanes by design)', + type: 'kanban', + data: { provider: 'object' as const, object: 'showcase_semantic_zoo_legacy' }, + columns: ['name', 'amount'], + }, +}); From 20bfa297f070ba3448da4362b8dd4ffde43ff295 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 16 Jul 2026 14:06:41 +0000 Subject: [PATCH 3/3] revert(showcase): drop unverified stageField kanban dogfood views MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two board views didn't resolve via the console's ?view= path (top-level defineView boards don't land in objectDef.listViews and the objects aren't in the app nav), so the browser assertion couldn't reliably verify them. The kanban default-lane behavior (objectui#2596) is already covered by that PR's unit tests plus semantic-roles.dogfood.test.ts (which proves stageField:'status' and stageField:false serve correctly — the exact inputs the console's detector consumes). Keeping only the pin bump. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_016wpaFMPowSn4nAWsjeGZib --- .../app-showcase/e2e/detail-shapes.spec.ts | 21 ---------- examples/app-showcase/objectstack.config.ts | 4 +- examples/app-showcase/src/ui/views/index.ts | 1 - .../src/ui/views/semantic-zoo.view.ts | 38 ------------------- 4 files changed, 2 insertions(+), 62 deletions(-) delete mode 100644 examples/app-showcase/src/ui/views/semantic-zoo.view.ts diff --git a/examples/app-showcase/e2e/detail-shapes.spec.ts b/examples/app-showcase/e2e/detail-shapes.spec.ts index 3ad7610ed..9bfabe658 100644 --- a/examples/app-showcase/e2e/detail-shapes.spec.ts +++ b/examples/app-showcase/e2e/detail-shapes.spec.ts @@ -137,27 +137,6 @@ test('stageField:false: status renders as a plain field, no stepper', async ({ p expect(errors, 'uncaught page errors on stageField:false detail').toEqual([]); }); -test('kanban default lanes honour stageField — role drives lanes, strict-false suppresses them', async ({ page }) => { - // Both `board` views deliberately carry NO kanban binding (spec-optional), - // so the lane field resolves through the shared stageField detector - // (objectui#2596). - - // Positive: zoo declares stageField:'status' → lanes are the option - // labels, with zero view-level config. - let errors = await openRecord(page, `/_console/apps/${APP}/showcase_semantic_zoo?view=board`); - await expect(page.getByText('Draft', { exact: true }).first()).toBeVisible(); - await expect(page.getByText('Done', { exact: true }).first()).toBeVisible(); - expect(errors, 'uncaught page errors on stageField board').toEqual([]); - - // Negative: legacy zoo declares stageField:false (its status is a color, - // not a lifecycle) → NO default lanes. The pre-#2596 hard-coded 'status' - // fallback would have grouped by Red / Green. - errors = await openRecord(page, `/_console/apps/${APP}/showcase_semantic_zoo_legacy?view=board`); - await expect(page.getByText('Red', { exact: true })).toHaveCount(0); - await expect(page.getByText('Green', { exact: true })).toHaveCount(0); - expect(errors, 'uncaught page errors on suppressed board').toEqual([]); -}); - test('ungrouped + related-heavy: flat details and related-list tabs on Contoso', async ({ page }) => { const errors = await openRecord(page, recordUrl('showcase_account', contosoId)); diff --git a/examples/app-showcase/objectstack.config.ts b/examples/app-showcase/objectstack.config.ts index 3ce1ea4fa..0d9b1f6eb 100644 --- a/examples/app-showcase/objectstack.config.ts +++ b/examples/app-showcase/objectstack.config.ts @@ -18,7 +18,7 @@ import { ExternalCustomer, ExternalOrder } from './src/data/objects/external/ind import { setupShowcaseExternalDatasource } from './src/system/datasources/external-fixture.js'; import { registerRecalcEndpoint } from './src/system/server/recalc-endpoint.js'; import { registerShowcasePositionBindings } from './src/security/bind-position-sets.js'; -import { TaskViews, ProjectViews, InquiryViews, BusinessUnitViews, SemanticZooViews, SemanticZooLegacyViews } from './src/ui/views/index.js'; +import { TaskViews, ProjectViews, InquiryViews, BusinessUnitViews } from './src/ui/views/index.js'; import { ShowcaseApp } from './src/ui/apps/index.js'; import { ChartGalleryDashboard, OpsDashboard, RevenuePulseDashboard } from './src/ui/dashboards/index.js'; import { ShowcaseTaskDataset, ShowcaseProjectDataset, ShowcaseInvoiceDataset, ShowcaseAccountDataset } from './src/ui/datasets/index.js'; @@ -181,7 +181,7 @@ export default defineStack({ // UI apps: [ShowcaseApp], portals: allPortals, - views: [TaskViews, ProjectViews, InquiryViews, BusinessUnitViews, SemanticZooViews, SemanticZooLegacyViews], + views: [TaskViews, ProjectViews, InquiryViews, BusinessUnitViews], pages: [CapabilityMapPage, StartHerePage, ComponentGalleryPage, ProjectWorkspacePage, ProjectDetailPage, TaskWorkbenchPage, TaskTriagePage, TaskBoardPage, TaskCalendarPage, TaskGalleryPage, TaskSchedulePage, TaskTimelinePage, TaskMapPage, TaskAllViewsPage, ActiveProjectsPage, TaskDetailPage, ReviewQueuePage, NewProjectWizardPage, MyWorkPage, SettingsPage, StylingGalleryPage, CommandCenterPage, CommandCenterJsxPage, CrmWorkbenchPage, TaskDeskPage, PageVariablesPage, ContactFormPage, RenewalsPipelinePage], dashboards: [ChartGalleryDashboard, OpsDashboard, RevenuePulseDashboard], books: allBooks, diff --git a/examples/app-showcase/src/ui/views/index.ts b/examples/app-showcase/src/ui/views/index.ts index c20d426ab..ef8f99f0f 100644 --- a/examples/app-showcase/src/ui/views/index.ts +++ b/examples/app-showcase/src/ui/views/index.ts @@ -5,4 +5,3 @@ export { ProjectViews } from './project.view.js'; export { InquiryViews } from './inquiry.view.js'; export { BusinessUnitViews } from './business-unit.view.js'; export { ContactViews } from './contact.view.js'; -export { SemanticZooViews, SemanticZooLegacyViews } from './semantic-zoo.view.js'; diff --git a/examples/app-showcase/src/ui/views/semantic-zoo.view.ts b/examples/app-showcase/src/ui/views/semantic-zoo.view.ts deleted file mode 100644 index 5590081b7..000000000 --- a/examples/app-showcase/src/ui/views/semantic-zoo.view.ts +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. - -import { defineView } from '@objectstack/spec'; - -/** - * Semantic-zoo kanban views — runtime dogfood for the ADR-0085 `stageField` - * role driving DEFAULT kanban lanes (objectui#2596). - * - * Both boards deliberately omit the `kanban` binding (it is optional on the - * view schema): with no explicit `groupByField`, the lane field resolves - * through the shared `detectStatusField` semantics — - * - * - `showcase_semantic_zoo` declares `stageField: 'status'` → the board - * lanes by Draft / Active / Done with zero view-level config; - * - `showcase_semantic_zoo_legacy` declares `stageField: false` (its - * `status` is a color, not a lifecycle) → NO default lanes: the board - * renders its empty state instead of grouping by Red / Green, which is - * exactly what the pre-#2596 hard-coded 'status' fallback used to do. - * - * Guarded by `examples/app-showcase/e2e/detail-shapes.spec.ts`. - */ -export const SemanticZooViews = defineView({ - board: { - label: 'Board (lanes from stageField)', - type: 'kanban', - data: { provider: 'object' as const, object: 'showcase_semantic_zoo' }, - columns: ['name', 'code', 'amount'], - }, -}); - -export const SemanticZooLegacyViews = defineView({ - board: { - label: 'Board (no lanes by design)', - type: 'kanban', - data: { provider: 'object' as const, object: 'showcase_semantic_zoo_legacy' }, - columns: ['name', 'amount'], - }, -});