Skip to content

Commit a56cc6d

Browse files
committed
docs
1 parent d8c5374 commit a56cc6d

14 files changed

Lines changed: 334 additions & 45 deletions

File tree

.changeset/kind-terms-smash.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
"@objectstack/mcp": minor
2+
"@objectstack/mcp": patch
33
---
44

55
ai

CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added — ObjectUI one-month frontend scan + backend alignment summary
11+
12+
Scanning `../objectui` from 2026-05-08 through 2026-06-08 shows a broad Studio
13+
and renderer push, not just the most recent commits. Major frontend changes
14+
include live field-level conditional rules in forms (`visibleWhen`,
15+
`readonlyWhen`, `requiredWhen`), row-scoped inline-grid rules,
16+
required-on-submit handling, spreadsheet-style master-detail line-item editing,
17+
atomic parent+child form saves, derived related lists, record-page authoring,
18+
page block/property inspectors, flow-designer typed config panels and
19+
simulator, dataset designer/preview, dataset-bound dashboard widgets and
20+
reports, report matrix/joined/drill-down renderers, dashboard drill-downs and
21+
type-aware cells, metadata-admin draft/publish/rollback and skew protection,
22+
AI draft review/publish surfaces, public/developer API pages, settings,
23+
notifications/approvals, marketplace/package management, and wide i18n polish.
24+
25+
The scan covered 910 non-merge commits in `../objectui`: 369 `feat`, 213 `fix`,
26+
108 `chore`, 37 `refactor`, plus docs/tests/CI/perf work. The largest touched
27+
areas were `packages/app-shell`, `apps/console`, `packages/plugin-detail`,
28+
`packages/plugin-dashboard`, `packages/plugin-grid`, `packages/fields`,
29+
`packages/plugin-report`, `packages/plugin-form`, `packages/plugin-list`, and
30+
`skills/objectui`.
31+
32+
The framework backend now mirrors the critical enforcement boundaries:
33+
`@objectstack/plugin-security` exposes `security.getReadFilter(object,
34+
context)` as a reusable RLS read scope, `@objectstack/service-analytics`
35+
auto-bridges to it for dataset/dashboard/report queries and fails closed when
36+
scope cannot be safely applied, and ObjectQL enforces field conditional rules
37+
by validating `requiredWhen` and ignoring writes to fields locked by
38+
`readonlyWhen`.
39+
1040
### Fixed — First-boot platform-admin promotion no longer stolen by the `usr_system` seed identity
1141

1242
`5e831dea3` (#1392) added `ensureSeedIdentity` to the runtime SeedLoader, which

content/docs/concepts/implementation-status.mdx

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ This matrix is generated from actual codebase analysis and represents the curren
186186

187187
| Protocol | Implementation | Status | Notes |
188188
|:---------|:-------------:|:------:|:------|
189-
| **Analytics** ||| Protocol implemented in ObjectQL (driver-level aggregation) |
189+
| **Analytics** ||| ObjectQL aggregation plus `@objectstack/service-analytics` dataset execution; analytics read scope auto-bridges to `security.getReadFilter` for RLS-aware dashboards/reports |
190190
| **OData** || 📋 | Protocol defined, not implemented |
191191
| **GraphQL** || 📋 | Protocol defined, not implemented |
192192
| **Realtime** || 📋 | Protocol defined, not implemented |
@@ -200,24 +200,35 @@ This matrix is generated from actual codebase analysis and represents the curren
200200

201201
| Protocol | @objectstack/spec | Implementation | Status |
202202
|:---------|:-----------------:|:-------------:|:------:|
203-
| **View** || | ❌ Spec only |
204-
| **ListView** || | ❌ Spec only |
205-
| **FormView** || | ❌ Spec only |
206-
| **Page** || | ❌ Spec only |
207-
| **App** || | ❌ Spec only |
208-
| **Dashboard** || | ❌ Spec only |
209-
| **Report** || | ❌ Spec only |
210-
| **Action** || | ❌ Spec only |
211-
| **Component** || | ❌ Spec only |
203+
| **View** || 🟡 | 🟡 Studio/ObjectUI renders authored metadata surfaces; general-purpose renderer coverage is still incomplete |
204+
| **ListView** || 🟡 | 🟡 ObjectUI grid/list surfaces support per-view persistence, filters, row actions, density, galleries, and permission-aware affordances; full protocol parity is ongoing |
205+
| **FormView** || 🟡 | 🟡 ObjectUI forms support live field rules (`visibleWhen` / `readonlyWhen` / `requiredWhen`), inline-grid row rules, and server-aligned required enforcement |
206+
| **Page** || 🟡 | 🟡 Record-page authoring, page create flows, block canvas editing, slotted record pages, and selected page blocks are implemented in ObjectUI; full component catalogue remains in progress |
207+
| **App** || 🟡 | 🟡 Navigation metadata is consumed by the console/app shell; full renderer parity remains in progress |
208+
| **Dashboard** || 🟡 | 🟡 ObjectUI renders metric/chart/list/pivot/funnel/table widgets, drill-downs, type-aware cells, and dataset-bound widgets; Studio can author per-widget dataset bindings (`dataset` / `dimensions` / `values`) |
209+
| **Report** || 🟡 | 🟡 ObjectUI renders spec-native tabular/summary/matrix/joined reports, chart/KPI blocks, drill-downs, and dataset-bound reports |
210+
| **Action** || 🟡 | 🟡 ObjectUI supports row/global/header actions, action modal transport, parameter collection, visibility CEL, and nested action runners |
211+
| **Component** || 🟡 | 🟡 Selected page/record components render in ObjectUI (`record:alert`, related lists, highlights, page blocks); catalogue parity remains in progress |
212212
| **Theme** ||| ❌ Spec only |
213-
| **Widget** || | ❌ Spec only |
214-
| **Chart** || | ❌ Spec only |
213+
| **Widget** || 🟡 | 🟡 Dashboard/report/list/form widgets render in ObjectUI; custom widget manifest/runtime parity remains in progress |
214+
| **Chart** || 🟡 | 🟡 Recharts-backed dashboard/report charts cover bar/line/pie/area/scatter/funnel/gauge/treemap/sankey with formatting and field-coverage guards |
215215

216216
**Notes:**
217217
- All UI protocols are fully defined in spec
218-
- Metadata API can retrieve UI definitions (views, apps)
219-
- No built-in UI renderer yet - designed for custom implementations
220-
- React SDK provides data hooks, UI rendering is application responsibility
218+
- Metadata API can retrieve UI definitions (views, apps, pages, dashboards,
219+
reports, actions)
220+
- Active Studio/ObjectUI source lives in the sibling `../objectui` repo and is
221+
bundled into `packages/console` via the objectui refresh workflow
222+
- Metadata-admin curated forms now merge server-only fields back into the save
223+
payload, so a Studio form that exposes only part of a metadata type should
224+
not strip newer protocol properties
225+
- The 2026-05-08 → 2026-06-08 ObjectUI scan covered 910 non-merge frontend
226+
commits. The largest functional clusters were metadata-admin/Studio,
227+
master-detail forms, reports/dashboards, record detail/related lists,
228+
action transport, flow designer, developer/API pages, AI draft publishing,
229+
and i18n.
230+
- React SDK provides data hooks; the cross-surface renderer is still being
231+
completed in ObjectUI rather than in this backend repo
221232

222233
---
223234

@@ -273,7 +284,7 @@ The `auth` service in `CoreServiceName` covers both **authentication** (identity
273284
- Client SDK supports bearer token header — but token validation requires the auth plugin
274285
- Auth route (`/auth/*`) only appears in Discovery when the auth plugin is registered
275286
- Fine-grained authorization (RLS, sharing, territory) is internal to the auth plugin
276-
- **Phase-1 RBAC enforcement is live end-to-end**: REST → ObjectQL → SecurityPlugin middleware now receives a populated `ExecutionContext` (userId, tenantId, roles, permissions). Default `member_default` permission set ships a wildcard RLS rule `organization_id = current_user.organization_id` plus per-object overrides `sys_organization_self` (`id = current_user.organization_id`) and `sys_user_self` (`id = current_user.id`) for the global tables that lack an `organization_id` column. The earlier `tenantField` indirection (RLS expressions written against an abstract `tenant_id` column then rewritten to the configured physical column at compile time) was removed — the placeholder, the column name, and `RLSUserContext.organization_id` are now the same name end-to-end. The legacy `objectql.registerTenantMiddleware` (hardcoded `where.tenant_id` injection that pre-dated SecurityPlugin) has been removed; SecurityPlugin is the sole authority for tenant isolation. Verified cross-organization isolation on `pnpm dev:crm` across `sys_organization`, `sys_member`, `sys_user`, `sys_user_permission_set`, `sys_role_permission_set`. **Anonymous traffic still bypasses enforcement** until a default-deny pass lands.
287+
- **Phase-1 RBAC enforcement is live end-to-end**: REST → ObjectQL → SecurityPlugin middleware now receives a populated `ExecutionContext` (userId, tenantId, roles, permissions). Default `member_default` permission set ships a wildcard RLS rule `organization_id = current_user.organization_id` plus per-object overrides `sys_organization_self` (`id = current_user.organization_id`) and `sys_user_self` (`id = current_user.id`) for the global tables that lack an `organization_id` column. The earlier `tenantField` indirection (RLS expressions written against an abstract `tenant_id` column then rewritten to the configured physical column at compile time) was removed — the placeholder, the column name, and `RLSUserContext.organization_id` are now the same name end-to-end. The legacy `objectql.registerTenantMiddleware` (hardcoded `where.tenant_id` injection that pre-dated SecurityPlugin) has been removed; SecurityPlugin is the sole authority for tenant isolation. Analytics now uses the same reusable read scope via `security.getReadFilter`, so dataset-bound dashboards/reports do not bypass RLS. Verified cross-organization isolation on `pnpm dev:crm` across `sys_organization`, `sys_member`, `sys_user`, `sys_user_permission_set`, `sys_role_permission_set`. **Anonymous traffic still bypasses enforcement** until a default-deny pass lands.
277288

278289
---
279290

@@ -373,11 +384,19 @@ The `auth` service in `CoreServiceName` covers both **authentication** (identity
373384
- [ ] Realtime Subscriptions
374385
- [ ] WebSocket Support
375386

376-
### Phase 7: UI Layer 📋 **PLANNED**
387+
### Phase 7: UI Layer 🟡 **IN PROGRESS**
377388
- [ ] UI Renderer
378-
- [ ] Form Builder
379-
- [ ] List View Renderer
380-
- [ ] Dashboard Renderer
389+
- [x] Studio metadata-admin engine — generic metadata list/detail/edit surfaces, live preview, draft/publish/rollback, create-mode forms, server-side diagnostics, package scoping, and skew-safe curated inspectors
390+
- [x] Form conditional rules — ObjectUI supports `visibleWhen`, `readonlyWhen`, `requiredWhen`, row-scoped inline-grid rules, and required-on-submit enforcement
391+
- [x] Master-detail forms — inline subforms, spreadsheet-style line items, atomic batch create/edit, lookup auto-fill, line ordering, duplicate/reorder, and subtotal/tax/total stack
392+
- [x] Record detail + related lists — derived related lists, record-page assignment, action slots, system/audit sections, and opt-in reference rail
393+
- [x] Dashboard renderer/editor — widget layout persistence, drill-downs, pivot/funnel/table/list widgets, type-aware cells, and per-widget dataset binding authoring (`dataset`, `dimensions`, `values`)
394+
- [x] Report renderer/editor — spec-native summary/matrix/joined reports, chart/KPI blocks, drill-downs, dataset-bound reports, and i18n labels
395+
- [x] Flow/page designers — typed flow node config panels, simulator/debug runner, page block canvas, slotted record pages, and schema-driven block inspectors
396+
- [x] Action transport — row/global/header actions, modal parameter collection, visible CEL, popup-safe open actions, and nested action runner sharing
397+
- [x] Developer/admin console surfaces — Integrations & APIs, public forms, flow runs, approvals inbox, settings, marketplace/package management, AI draft review/publish
398+
- [ ] List View Renderer full protocol parity
399+
- [ ] Cross-surface UI Renderer full protocol parity
381400
- [ ] Theme Engine
382401

383402
### Phase 8: Automation (Plugin) 🟡 **IN PROGRESS**
@@ -390,6 +409,7 @@ The `auth` service in `CoreServiceName` covers both **authentication** (identity
390409
- [x] Authentication Plugin (`@objectstack/plugin-auth`, better-auth)
391410
- [x] Authorization Plugin — `@objectstack/plugin-security` enforces CRUD/FLS/RLS in the ObjectQL middleware chain; REST → ObjectQL now propagates `ExecutionContext` end-to-end (Phase-1)
392411
- [x] Row-Level Security — default `member_default` permission set applies a wildcard `organization_id = current_user.organization_id` rule plus per-object overrides `sys_organization_self` / `sys_user_self`. The earlier `tenantField` rewrite indirection was removed: RLS column, placeholder, and `RLSUserContext.organization_id` use the same canonical name end-to-end
412+
- [x] Analytics RLS bridge — `@objectstack/service-analytics` auto-bridges to `security.getReadFilter(object, context)` and fails closed when read-scope resolution cannot be safely applied
393413
- [x] Multi-tenancy — verified cross-organization isolation on `pnpm dev:crm` (Alice@OrgAlpha vs. Bob@OrgBeta only see their own records across `sys_organization`, `sys_member`, `sys_user`, and `sys_*_permission_set` link tables)
394414
- [x] Legacy `objectql.registerTenantMiddleware` removed — SecurityPlugin is now the sole tenant-isolation authority
395415
- [ ] Default-deny for anonymous traffic

content/docs/guides/cheatsheets/field-type-gallery.mdx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,10 @@ These properties are available on **all** field types:
631631
| `maskingRule` | `object` | — | Data masking configuration |
632632
| `dependencies` | `object` | — | Dependent field configuration |
633633
| `dataQuality` | `object` | — | Data quality validation rules |
634-
| `conditionalRequired` | `object` | — | Conditional requirement rules |
634+
| `visibleWhen` | `Expression` | — | Show field only when predicate is true |
635+
| `readonlyWhen` | `Expression` | — | Make field read-only when predicate is true |
636+
| `requiredWhen` | `Expression` | — | Require field when predicate is true |
637+
| `conditionalRequired` | `Expression` | — | Deprecated alias of `requiredWhen` |
635638
636639
---
637640

content/docs/guides/cheatsheets/field-validation-rules.mdx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ These properties apply to **all** field types and are validated by the base `Fie
3232
| `index` | `boolean` | `false` | Creates standard database index |
3333
| `externalId` | `boolean` | `false` | Marks field as external ID for upsert operations |
3434
| `auditTrail` | `boolean` | `false` | Tracks all changes with user and timestamp |
35-
| `conditionalRequired` | `string` || Formula expression that makes field required when `TRUE` |
35+
| `visibleWhen` | `string \| Expression` || CEL predicate; field is shown only when `TRUE` |
36+
| `readonlyWhen` | `string \| Expression` || CEL predicate; field is read-only when `TRUE` |
37+
| `requiredWhen` | `string \| Expression` || CEL predicate; field is required when `TRUE` |
38+
| `conditionalRequired` | `string \| Expression` || Deprecated alias of `requiredWhen` |
3639

3740
---
3841

content/docs/guides/formula.mdx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ ObjectStack uses a **single canonical expression language** for every place a
99
piece of metadata needs to compute a value or evaluate a condition:
1010

1111
- **Formula fields** (`type: 'formula'`)
12-
- **Predicates** — validation `condition`, sharing `condition`, conditional
13-
visibility (`visibleOn`), conditional required (`conditionalRequired`),
14-
action `disabled`, view filter `criteria`, hook `condition`, flow decisions
12+
- **Predicates** — validation `condition`, sharing `condition`, field
13+
conditional rules (`visibleWhen`, `readonlyWhen`, `requiredWhen`),
14+
conditional visibility (`visibleOn`), action `disabled`, view filter
15+
`criteria`, hook `condition`, flow decisions
1516
- **Dynamic seed values** — fixtures whose value depends on the install-time
1617
clock or identity context
1718

@@ -92,7 +93,7 @@ export const Invoice = ObjectSchema.create({
9293
formula: F`record.subtotal + record.subtotal * record.tax_rate`,
9394
}),
9495
po_number: Field.text({
95-
conditionalRequired: P`record.amount > 10000`,
96+
requiredWhen: P`record.amount > 10000`,
9697
}),
9798
// M9.9b — declarative default, evaluated at insert time
9899
issued_date: Field.date({
@@ -196,14 +197,20 @@ CEL throws on `null + string`, so wrap every nullable operand in
196197
}
197198
```
198199

199-
### Predicate (visibility, conditional required)
200+
### Predicate (visibility, read-only, conditional required)
200201

201202
```ts
202203
{
203204
name: 'rating',
204205
type: 'picklist',
205206
visibleOn: P`record.status == 'qualified'`,
206207
}
208+
209+
Field.text({
210+
visibleWhen: P`record.type == 'business'`,
211+
readonlyWhen: P`record.status == 'approved'`,
212+
requiredWhen: P`record.amount > 10000`,
213+
})
207214
```
208215

209216
### Dynamic seed dates

content/docs/guides/metadata/dashboard.mdx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,74 @@ Each widget defines a data visualization:
9898
| `options` | `object` | optional | Additional display options |
9999
| `responsive` | `object` | optional | Responsive behavior |
100100

101+
## Dataset-Bound Widgets
102+
103+
Dashboards support two additive query shapes:
104+
105+
- **Inline widgets** use `object`, `categoryField`, `valueField`, `aggregate`,
106+
and `filter` directly on the widget.
107+
- **Dataset-bound widgets** use the semantic-layer `dataset` plus named
108+
`dimensions` and `values`.
109+
110+
Prefer dataset-bound widgets for shared business metrics. A dataset defines
111+
the base object, allowed relationship joins, reusable dimensions, certified
112+
measures, and intrinsic filters once; dashboards and reports then reference
113+
the same names so "revenue", "pipeline", or "win rate" stay consistent across
114+
surfaces.
115+
116+
```typescript
117+
import { defineDataset } from '@objectstack/spec/ui';
118+
119+
export const salesDataset = defineDataset({
120+
name: 'sales',
121+
label: 'Sales',
122+
object: 'opportunity',
123+
include: ['account'],
124+
dimensions: [
125+
{ name: 'region', field: 'account.region', type: 'string' },
126+
{ name: 'close_month', field: 'close_date', type: 'date', dateGranularity: 'month' },
127+
],
128+
measures: [
129+
{ name: 'revenue', field: 'amount', aggregate: 'sum', certified: true },
130+
{ name: 'deal_count', aggregate: 'count' },
131+
],
132+
});
133+
134+
export const salesDashboard = {
135+
name: 'sales_overview',
136+
label: 'Sales Overview',
137+
widgets: [
138+
{
139+
id: 'revenue_by_region',
140+
title: 'Revenue by Region',
141+
type: 'bar',
142+
dataset: 'sales',
143+
dimensions: ['region'],
144+
values: ['revenue'],
145+
layout: { x: 0, y: 0, w: 6, h: 4 },
146+
},
147+
],
148+
};
149+
```
150+
151+
Dataset-bound widgets require at least one `values` entry. Do not also set
152+
`object`, `valueField`, or `aggregate` unless you are intentionally keeping an
153+
inline widget. Studio's Dashboard Widget Inspector can author the dataset,
154+
dimensions, and values per widget; curated metadata-admin forms preserve any
155+
server-only fields that are not exposed by the current inspector, so saving a
156+
dashboard from Studio should not strip newer protocol fields.
157+
158+
At runtime, dataset queries are executed through the analytics service. When
159+
`@objectstack/plugin-security` is registered, analytics auto-bridges to
160+
`security.getReadFilter(object, context)` and applies the caller's RLS scope to
161+
the base object and joined objects.
162+
163+
ObjectUI currently renders object-backed and dataset-backed metric, chart,
164+
table/list, pivot, funnel, and gauge-style widgets with type-aware cells,
165+
field/option labels, date bucketing, numeric/currency formatting, and
166+
drill-down affordances. Use declarative drill-down settings and preserve raw
167+
group keys so the generated filter can navigate to the underlying records.
168+
101169
### Chart Types
102170

103171
| Type | Description | Best For |

0 commit comments

Comments
 (0)