Skip to content

Commit ac46e52

Browse files
authored
docs+build(releases): whole-platform 12.0→14.x history + frontend-aware maintenance model (#2827)
Fill in the missing 12.0→14.x developer-facing release notes and fix why they went missing: a three-layer, mostly-automated release-history model. - Curated v12/v13/v14 release pages (backend + Console per platform version); index + meta updated. - bump-objectui.sh emits a @objectstack/console changeset per objectui pin bump so frontend deltas enter the changesets pipeline. - check-release-notes.mjs (CI) fails when a released spec major has no curated, navigable release page. - objectui-range.mjs summarizes the frontend delta for a version range. - Canonical-source banners on root CHANGELOG.md / RELEASE_NOTES.md; the model is documented in docs/releases-maintenance.md.
1 parent 7731f35 commit ac46e52

14 files changed

Lines changed: 1102 additions & 8 deletions

File tree

.github/workflows/lint.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ jobs:
7272
- name: Single authz resolver guard
7373
run: pnpm check:authz-resolver
7474

75+
# Release-notes drift guard: the platform is one version-locked train, so
76+
# every released @objectstack/spec major must have a curated, navigable
77+
# release page at content/docs/releases/v<major>.mdx. Catches the gap that
78+
# let v10–v14 ship with no page while spec was already at 14.x.
79+
- name: Release-notes drift guard
80+
run: pnpm check:release-notes
81+
7582
typecheck:
7683
name: TypeScript Type Check
7784
runs-on: ubuntu-latest

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,22 @@ All notable changes to the ObjectStack Protocol will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
> **Where release history now lives.** The platform ships as one version-locked
9+
> train (changesets `fixed` group), so the update history is maintained in three
10+
> layers rather than by hand-editing this file:
11+
>
12+
> - **Per-package detail** — each package's `CHANGELOG.md` is generated by
13+
> [changesets](https://github.com/changesets/changesets) from the `.changeset/`
14+
> entries every PR adds (including `@objectstack/console`, which now carries a
15+
> changeset for each bundled objectui/frontend bump).
16+
> - **Platform, developer-facing narrative** — the curated per-major pages under
17+
> [`content/docs/releases/`](content/docs/releases) lead with breaking changes +
18+
> migration and cover backend **and** Console (frontend). Start there.
19+
> - **This file** is retained for its historical entries. The `[Unreleased]`
20+
> backlog below predates the per-major release pages; its items are released in
21+
> 12.x–14.x and are being migrated into those pages — prefer them for anything
22+
> new. See `docs/releases-maintenance.md` for the full maintenance model.
23+
824
## [Unreleased]
925

1026
### Changed — Security: anonymous access denied by default (BREAKING) + destructive-op RBAC gates

RELEASE_NOTES.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Release Notes
22

3+
> **📓 This file is historical.** Current, developer-facing release notes live in
4+
> the curated per-major pages under [`content/docs/releases/`](content/docs/releases)
5+
> — they lead with breaking changes + migration and cover backend **and** Console
6+
> (frontend) per platform version. For exhaustive per-package detail, see each
7+
> package's changeset-generated `CHANGELOG.md`. The entries below are kept for
8+
> historical reference; see `docs/releases-maintenance.md` for how release history
9+
> is maintained.
10+
311
> **v5.0 (2026): `project``environment`.** The runtime concept formerly called *Project* (per-tenant business workspace; Org/Project/Branch hierarchy) is now called *Environment* throughout the codebase: CLI flags (`--environment`/`-e`), HTTP paths (`/api/v1/environments/:environmentId/...`), headers (`X-Environment-Id`), env vars (`OS_ENVIRONMENT_ID`), exported symbols (`createSystemEnvironmentPlugin`, `SYSTEM_ENVIRONMENT_ID`), DB columns (`environment_id`), and JSON schemas (`EnvironmentArtifact`) all use the new term with no aliases or deprecation shims. The word "project" elsewhere in this document still refers to the npm/monorepo sense (i.e., the framework as a software project). See `.changeset/v5-project-to-environment-rename.md` for the full breaking-change list and ADR-0006 for the rationale.
412
513

content/docs/releases/index.mdx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,14 @@ migration steps, then covers new capabilities and notable fixes.
1818

1919
## Versions
2020

21+
- [v14.0.0](/docs/releases/v14) — ADR-0090 vocabulary convergence completed, object `enable.*` flags become real gates, admin user management, phone/SMS auth, book-audience enforcement, data-lifecycle contract, and effective-dated grants (current series: 14.4.0).
22+
- [v13.0.0](/docs/releases/v13) — Permission Model v2 (ADR-0090): Roles and Profiles converge on Positions, custom objects default to private, plus an explain engine, delegated administration, and self-serve MCP OAuth.
23+
- [v12.0.0](/docs/releases/v12) — Anonymous data access denied by default (ADR-0056 D2), adaptive record surfaces, an enforced protocol-version handshake, and build-gating author-time lints.
2124
- [v9.0.0](/docs/releases/v9) — Analytics single-form cutover (ADR-0021), honest chart taxonomy, canonical `OS_*` settings env vars, Google sign-in, AI build experience.
2225

26+
> Curated notes for v10 and v11 are not yet written; consult the per-package
27+
> `CHANGELOG.md` files for those versions in the meantime.
28+
2329
## Where the fine-grained changelogs live
2430

2531
These pages are curated summaries. For exhaustive, per-package detail:

content/docs/releases/meta.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"title": "Releases",
33
"icon": "Tag",
4-
"pages": ["index", "v9", "implementation-status"]
4+
"pages": ["index", "v14", "v13", "v12", "v9", "implementation-status"]
55
}

content/docs/releases/v12.mdx

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
---
2+
title: v12.0.0
3+
description: Anonymous data access denied by default (ADR-0056 D2), adaptive record surfaces, an enforced protocol-version handshake, and a wave of author-time correctness lints.
4+
---
5+
6+
**Released July 2026.** All `@objectstack/*` packages are published at 12.0.0.
7+
This major flips the platform's default security posture — anonymous requests
8+
to the data API are now denied — and lands a wave of author-time correctness
9+
features: adaptive record surfaces derived from metadata, package-shipped
10+
permission sets, an enforced `engines.protocol` handshake, and build-gating
11+
lints for view references, form layouts, and import mappings.
12+
13+
**TL;DR for upgraders:** if any deployment serves data anonymously (a demo,
14+
kiosk, or public playground), you must now set `api: { requireAuth: false }`
15+
on the stack or every `/data/*` call returns HTTP 401. Everything else in this
16+
release is additive — existing metadata keeps loading.
17+
18+
## Breaking changes
19+
20+
### `api.requireAuth` defaults to `true` — anonymous data access is denied (ADR-0056 D2)
21+
22+
The global `requireAuth` default flipped from `false` to `true`
23+
(`RestApiConfigSchema.requireAuth` in `@objectstack/spec`, mirrored by
24+
`RestServer.normalizeConfig` in `@objectstack/rest`). Anonymous requests to the
25+
`/data/*` CRUD and batch endpoints are now rejected with **HTTP 401** unless
26+
the deployment explicitly opts out.
27+
28+
**Migration (one line).** A deployment that intentionally serves data publicly
29+
sets the flag on the stack config — now a declared
30+
`ObjectStackDefinitionSchema.api` field, so it survives `defineStack` strict
31+
parsing (an undeclared top-level `api` key was previously stripped silently):
32+
33+
```ts
34+
export default defineStack({
35+
//
36+
api: { requireAuth: false },
37+
});
38+
```
39+
40+
The REST plugin logs a boot warning for the explicit opt-out, so a fail-open
41+
posture is always visible. A misplaced `api.requireAuth` at the plugin level
42+
(one nesting short) is also called out with a boot warning instead of being
43+
ignored.
44+
45+
**What keeps working with no action:**
46+
47+
- **Share links** — validate their token, then read under a system context.
48+
- **Public forms** — self-authorizing via the declaration-derived
49+
`publicFormGrant` (create + read-back on the declared target object only).
50+
- **Control plane**`/auth`, `/health`, `/discovery` are exempt.
51+
- **`objectstack serve` with an auth-less stack** — the CLI passes an explicit
52+
`requireAuth: false` for stacks whose tier set has no `auth` (nothing could
53+
authenticate against them), with the boot warning.
54+
55+
> Scope note: this gate covers the REST `/data/*` surface. The metadata
56+
> read/write endpoints and the dispatcher GraphQL route have their own
57+
> pre-existing anonymous posture, tracked separately.
58+
59+
## New in the framework
60+
61+
### Adaptive record surfaces and semantic field spans (#2578)
62+
63+
Because all metadata is AI-authored, the design goal is to make the model
64+
unable to get layout wrong — so these features are **derived**, not authored:
65+
66+
- **`deriveRecordSurface` (ADR-0085 §5)** — a record's default surface (full
67+
`page` vs. `drawer`/`modal` overlay) is derived from how heavy the record is
68+
(visible, non-system field count; mobile always pages). No new object key; an
69+
explicit form/navigation config still wins.
70+
- **`FormField.span: 'auto' | 'full'`** — replaces absolute `colSpan` as the
71+
primary primitive. Under a per-surface derived column count (mobile 1 /
72+
modal 2 / page 3–4), `span` is decoupled from the count so a field lays out
73+
correctly at any width. `colSpan` is retained and clamped for back-compat.
74+
- **`NavigationConfig.size`** — a T-shirt bucket (`auto`/`sm`/`md`/`lg`/`xl`/
75+
`full`) replacing the pixel `width`/`drawerWidth`, which an AI author cannot
76+
choose blind. `auto` lets the renderer size from field count and clamp to the
77+
viewport.
78+
- **`validateFormLayout` lint** — advisory `form-field-unknown` (a section
79+
references a field not on the bound object) and `absolute-colspan-discouraged`.
80+
81+
`deriveRecordFlowSurface` (12.2.0) extends this to be flow-aware: `view` keeps
82+
the shipped behavior, while the `create`/`edit`/`child-*` task flows are always
83+
overlays (a task's URL is a false promise — a refresh loses the draft).
84+
85+
### Detail-page related lists: `'primary'` prominence (#2579)
86+
87+
`Field.relatedList` on a child's `lookup`/`master_detail` FK becomes a
88+
tri-state `boolean | 'primary'`. `'primary'` marks a core relationship that the
89+
detail page promotes to its own tab; non-primary children collapse into a
90+
single shared **Related** tab. `RecordRelatedListProps.columns` becomes
91+
optional — when omitted, columns derive from the child object's
92+
`highlightFields`. Purely additive and opt-in per relationship.
93+
94+
### Package-shipped permission sets (ADR-0086 P1)
95+
96+
Packages now ship working default access for their own objects, with a
97+
machine-checkable metadata↔config boundary:
98+
99+
- `PermissionSetSchema.packageId` (owning package) and per-record provenance
100+
`managedBy: 'package' | 'platform' | 'user'`, persisted on
101+
`sys_permission_set` as `package_id` / `managed_by`.
102+
- **`bootstrapDeclaredPermissions`** materializes `stack.permissions` into
103+
`sys_permission_set` at boot (`managed_by: 'package'`). Idempotent and
104+
upgrade-aware: seeder-owned rows are re-seeded to the shipped declaration;
105+
rows owned by a different package are refused loudly; env-authored rows are
106+
never clobbered. Closes the inert-metadata gap where declared sets were
107+
enforced at runtime but never materialized (invisible to the admin surface).
108+
109+
### The protocol-version handshake is now enforced (ADR-0087 P0)
110+
111+
`PluginEnginesSchema.protocol` was declared, documented, and checked by no
112+
loader or installer. Now:
113+
114+
- `@objectstack/spec` exports **`PROTOCOL_VERSION` / `PROTOCOL_MAJOR`** — the
115+
single source of truth, kept in lockstep with the package major by a drift
116+
test.
117+
- `@objectstack/metadata-core` adds `checkProtocolCompat()` /
118+
`assertProtocolCompat()` and the structured `ProtocolIncompatibleError`
119+
(`OS_PROTOCOL_INCOMPATIBLE`, carrying both versions and the
120+
`objectstack migrate meta --from N` command).
121+
- `installPackage` runs the handshake **before** writing to the registry — an
122+
incompatible package is refused with a machine-actionable diagnostic instead
123+
of crashing later in a schema `.parse()`.
124+
125+
Additive: packages that declare no `engines.protocol` range keep loading (with
126+
a warning).
127+
128+
### Named import mappings (#2611)
129+
130+
`POST /data/:object/import` accepts `mappingName`, resolving a registered
131+
`defineMapping` artifact (stack `mappings:`) and applying its `fieldMapping`
132+
pipeline (rename + constant/map/split/join; lookups delegate to the built-in
133+
reference resolution) as a strict projection before coercion. Errors are loud
134+
and specific (`MAPPING_NOT_FOUND`, `MAPPING_TARGET_MISMATCH`,
135+
`UNSUPPORTED_TRANSFORM` for `javascript`, …). `defineStack` cross-reference
136+
validation rejects mappings targeting undefined objects at build time.
137+
138+
### Smaller declarative additions
139+
140+
- **`ObjectNavItem.filters`** — declarative URL filter conditions
141+
(`filter[field]=value`) targeting the parameterized bare data surface, for
142+
one-off / parameterized slices (dashboard drill-throughs, "assigned to me").
143+
Values support the `{current_user_id}` / `{current_org_id}` template
144+
variables. Combining `filters` with `recordId`/`viewName` is now unwritable
145+
(correct-by-construction).
146+
- **`SelectOption.visibleWhen`** — a per-option CEL visibility predicate for
147+
`select`/`multiselect`/`radio`, expressing cascading / dependent options
148+
(`record.country == 'cn'`) and context gating without a bespoke matrix.
149+
- **`Action.order`** — an explicit ordering lever (lower = more prominent) for
150+
which action holds the record-header primary-button slot, instead of relying
151+
on fragile cross-file registration order. Fully backward compatible (unset =
152+
`0`, stable sort).
153+
- **Retired placeholder metadata kinds** (ADR-0088) — `trigger`, `router`,
154+
`function`, `service` are removed from `MetadataTypeSchema` (30 → 26 kinds);
155+
each had no authoring surface. The delivered replacements are `hook` /
156+
`record_change` flows, plugin `contributes.routes` + declarative `apis:`,
157+
`defineStack({ functions })`, and the plugin/service registry.
158+
159+
## Author-time correctness (lints that gate the build)
160+
161+
- **`lint-view-refs`** (#2554) — a `type: 'form'` action whose `target`
162+
resolves to a list view now **fails `os compile`** (the concrete breakage: a
163+
blank form, a silently no-op submit). Silent `<object>.<key>_2` rename
164+
collisions are surfaced as warnings. Shifts objectui's runtime `viewKind`
165+
guard left to compile time.
166+
- **`validateListViewMode`** (ADR-0053 phase 4) — `userFilters` on an object
167+
list view is now a `tsc` error via `ObjectListViewSchema`, stripped at parse
168+
for back-compat, and reported pre-parse with a fix hint.
169+
- **`transfer` / `restore` / `purge` pre-mapped to RBAC bits** (#1883) — the
170+
permission evaluator maps the destructive lifecycle operations to
171+
`allowTransfer` / `allowRestore` / `allowPurge` (with the `modifyAllRecords`
172+
bypass), so the gate exists ahead of the operations themselves (roadmap M2).
173+
Descriptions moved from `[EXPERIMENTAL — not enforced]` to `[RBAC-gated;
174+
operation pending M2]`.
175+
176+
## New in Console (Studio)
177+
178+
The Console build frozen into `@objectstack/console` 12.x renders the adaptive
179+
surfaces and related-list model this release derives:
180+
181+
- **Records open at the right size automatically** — the renderer consumes
182+
`deriveRecordSurface` / `deriveRecordFlowSurface`: field-heavy records open as
183+
a full page, light ones as a drawer/modal, and create/edit tasks always as an
184+
overlay that returns losslessly to its origin. An explicit form/navigation
185+
config still wins.
186+
- **Related lists gain structure** — a `relatedList: 'primary'` child gets its
187+
own detail-page tab while the rest collapse into one shared **Related** tab;
188+
every eligible FK surfaces its own list (a child referencing the parent
189+
through several relationships now shows each), self-referential hierarchies
190+
surface a "child" list, and the lookup picker and related list of the same
191+
object share one `highlightFields` column source.
192+
- **Parameterized nav landings** — an `ObjectNavItem.filters` entry resolves to
193+
the bare data surface (`/:object/data?filter[…]`) with removable filter chips,
194+
for drill-throughs and "assigned to me" links.
195+
- **Dependent picklists**`SelectOption.visibleWhen` hides options client-side
196+
as a UX affordance (the value is still authorized server-side).
197+
198+
For exhaustive, per-commit frontend detail, see the
199+
[objectui CHANGELOG](https://github.com/objectstack-ai/objectui/blob/main/CHANGELOG.md).
200+
201+
## Notable fixes
202+
203+
- **Package seed can no longer wedge the platform via record-change
204+
automation** (12.5.0). A seeded record whose lifecycle flow self-triggered
205+
looped forever because a boolean re-entry guard never tripped (booleans
206+
persist as integer `1` on SQLite/libsql and CEL `1 != true`). Fixed at three
207+
layers: `ExecutionContext.skipTriggers` for seed/bulk writes,
208+
`coerceBooleanFields()` on the after-hook view, and an `activeRecordFlows`
209+
self-trigger backstop in the automation engine.
210+
- **`Field.relatedList` JSDoc corrected** (12.1.0) — non-primary related lists
211+
stack under one shared "Related" tab; there is no count-based auto-split.
212+
213+
## Upgrade checklist
214+
215+
1. Update all `@objectstack/*` dependencies to `^12.0.0` (they are
216+
version-locked — upgrade them together).
217+
2. If any deployment serves data anonymously, add `api: { requireAuth: false }`
218+
to its `defineStack` config and confirm the boot warning appears.
219+
3. Re-run `os compile` / `os validate` — the new view-reference, list-view-mode,
220+
and form-layout lints may flag pre-existing authoring mistakes.
221+
4. Declare `engines.protocol` on any package you ship so the handshake
222+
advertises a real range instead of grandfathering with a warning.

0 commit comments

Comments
 (0)