Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
aabc527
chore(deps): resolve @objectstack/spec at 17.3.0 in the lockfile
os-sam Sep 5, 2026
2d11879
Merge remote-tracking branch 'origin/main' into claude/issue-7122-spe…
claude Sep 5, 2026
952d2ec
fix(app-shell,scripts): reconcile the declared-symbol surface with @o…
claude Sep 5, 2026
59ea811
test(types,plugin-form,auth): re-point the citation-stripped pins at …
claude Sep 5, 2026
dcd0dfa
chore(changeset): declare the app-shell/auth reconciliation the 17.3.…
claude Sep 5, 2026
ed37005
fix(core): re-derive the spec action-key inventory for 17.3.0's `oper…
claude Sep 5, 2026
36baa6d
docs(auth): state the preview-mode provenance without a version liter…
claude Sep 5, 2026
1546e59
chore(deps): resolve the @objectstack/* family at 17.3.0 so the spec …
os-sam Sep 5, 2026
33abf58
test(types): re-point the select-option pins at 17.3.0's authoring bo…
os-sam Sep 5, 2026
f389bec
chore(deps): collapse the postcss duplicate the first lockfile bump i…
os-sam Sep 5, 2026
339885d
Merge remote-tracking branch 'origin/main' into claude/issue-7122-spe…
claude Sep 5, 2026
9c1a1ac
wip(spec-17.3.0): parity reconciliation in progress — INCOMPLETE AND …
claude Sep 5, 2026
fa73d58
Merge remote-tracking branch 'origin/main' into claude/issue-7122-spe…
claude Sep 5, 2026
2761082
wip(spec-17.3.0): two files in progress — INCOMPLETE AND UNREVIEWED
claude Sep 5, 2026
dc82492
fix(spec-17.3.0): carry the ninth platform capability, and pin refusa…
claude Sep 5, 2026
e328e36
fix(spec-17.3.0): harvest three exemptions the contract outgrew, and …
claude Sep 5, 2026
5ac59ff
Merge remote-tracking branch 'origin/main' into claude/issue-7122-spe…
claude Sep 6, 2026
34a1578
docs(changeset): describe the 17.3.0 capability carry and the pin re-…
claude Sep 6, 2026
901c4b8
chore(budget): raise the eager-closure ceiling once, on maintainer au…
claude Sep 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .changeset/7122-capability-manage-org-presentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
'@object-ui/fields': patch
'@object-ui/i18n': patch
---

Carry `manage_org_presentation`, the ninth platform capability (objectui#7122).

`@objectstack/spec` 17.3.0 declares a ninth member of `PLATFORM_CAPABILITIES`
and the capability picker's curated set carried eight, so
`CapabilityMultiSelectField` fell back to the `sys_capability` registry's
English label for it in every locale — the exact defect objectui#6285 filed
when `manage_sharing` did the same thing.

The label is the spec artifact's own (`Manage Organization Presentation`), read
off the installed build rather than invented, and it is authored everywhere the
widget's docblock requires of any edit to that list: `useFieldTranslation.ts`
and all ten locale packs. Each non-English string is composed from that pack's
own established sibling vocabulary (`manage_org_users`,
`manage_platform_settings`) rather than machine-translated; a native review pass
is welcome on the nine, and nothing about the capability's behaviour depends on
the wording.

The parity pin is unchanged and still fails on ANY difference in either
direction, which is what made this visible before it reached a screen.
50 changes: 50 additions & 0 deletions .changeset/7122-eager-closure-ceiling-raise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
---

Raise the console eager-closure ceiling once, on explicit maintainer
authorisation, to the measured cost of `@objectstack/spec` 17.3.0
(objectui#7122). Tooling only; no package is released by this change.

Raising a gate ceiling is a gate weakening and sits on the manual floor, so it
is a human's decision. The authorisation, verbatim:

维护者 sam@objectstack.ai 于本轮明确授权:「抬上限,把 7685 弄绿」

recorded on objectui#7122 as decision batch 1 item 1 = "B + A": a one-time,
cause-recorded adjustment of exactly the measured delta, with the upstream card
filed alongside it.

**What the bytes buy — this is NOT routine growth.** The residue is
`@objectstack/spec` 17.3.0's own browser-dist growth: +292.2 KB gzip on the spec
package alone, whose measured mechanism is that 17.3.0 lengthened the Zod
`.describe()` doc strings shipped in the browser build. It is authoring
documentation prose, delivered on every page load.

**It is not duplication.** That was the larger, separate problem and it is
already fixed: resolving the spec alone to 17.3.0 shipped two copies of it, and
moving the family together in the lockfile collapsed that (−671 KB; markers
unique to 17.2.0 fell from 92.3% to 1.0% of 104, the single survivor accounted
for). No chunk entered or left the closure and the eager chunk count is
unchanged.

**The honest long-term fix is upstream**, and this ceiling is the marker for it
rather than the answer to it: a `describe()`-stripped browser build returns
~292 KB to every consumer of the spec, not just this console. Filed as
objectstack#16063, and recorded in the gate as the restore condition — when it
lands, re-measure and bring the ceiling and its baseline back down together.

Four constants move as two ceiling/baseline pairs, measured by `pnpm build`
(exit 0, 43/43) on `34a1578ef`:

| constant | from | to | headroom vs the 91,136-byte regression |
|---|---|---|---|
| `MAX_EAGER_CLOSURE_GZIP_BYTES` | 3,268,000 | 3,597,000 | 45,809 = 0.50x |
| `BASELINE.gzipBytes` | 3,222,314 | 3,551,191 | — |
| `PER_CHUNK_GZIP_CEILINGS['vendor-objectstack']` | 967,000 | 1,254,000 | 18,971 = 0.21x |
| `PER_CHUNK_BASELINE['vendor-objectstack']` | 948,461 | 1,235,029 | — |

Both ratios match the proportions the retiring pairs carried (0.50x and 0.20x),
so the gate keeps failing on a repeat of the 89 KiB regression that motivated
it. No other exemption was added, no import was made lazy, and the three
per-chunk lines that still pass — `i18n-locales`, `framework`, `ui-components` —
were left exactly as they are.
28 changes: 28 additions & 0 deletions .changeset/7122-lookup-target-required-before-save.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
'@object-ui/app-shell': patch
'@object-ui/plugin-designer': patch
---

Refuse to save a `lookup` / `master_detail` field with no target, instead of
PUTting it and blocking the object (objectui#7122).

`@objectstack/spec` 17.3.0 makes `reference` a hard requirement on the two
relationship field types — a `custom` refinement at path `reference`, measured
on the installed build; at 17.2.0 the requirement was prose only and
`{ type: 'lookup', label: 'L' }` parsed green. The designer relied on that
latitude and PUT half-filled drafts.

Against a matched 17.3.0 backend that PUT returns `422 INVALID_METADATA` for the
WHOLE object document, so the damage is not confined to the incomplete field:
every later save of that object fails the same way until the draft is completed
or removed by hand.

Both metadata writers now raise before the request — `MetadataService.saveFields`
and `MetadataFieldsPage`'s own field-map conversion — naming the field and what
to do about it. The message lands in the page's existing error banner, the same
one a nameless or duplicated field already produces; no new UI affordance, and
no request is issued. A relationship field WITH a target is unaffected.

Picked over the cheaper alternative deliberately: flipping the parity pin green
while the product still PUT the draft would have pinned a known-broken save
path.
22 changes: 22 additions & 0 deletions .changeset/7122-objectstack-family-17-3-0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
'@object-ui/data-objectstack': patch
---

Reconcile `@object-ui/data-objectstack` with the `@objectstack/*` family at
17.3.0 (objectui#7122).

`@objectstack/client`, `core`, `formula` and `lint` each pin `@objectstack/spec`
EXACTLY, so resolving the spec alone to 17.3.0 left the console bundling TWO
copies of it. Moving the family with it in `pnpm-lock.yaml` collapses the
duplicate; every declared range already admitted 17.3.0, so no manifest moved.

The one source change the family bump forces is a type reconciliation, not a
behaviour change. `client.analytics.query` resolved to `Promise<any>` at 17.2.0
and resolves to `Promise<AnalyticsResult>` at 17.3.0, so the pre-envelope
branches of `aggregate`'s row-shape fallback stopped type-checking. Those
branches are read through a widened alias rather than deleted: the client's own
docblock records the runtime change behind the narrower type ("BREAKING since
objectstack#13079 — read `result.rows`, not `result.data.rows`"), and deleting
them is a runtime compatibility decision about servers older than that, not a
type repair. The alias restores exactly the compile-time latitude 17.2.0 gave
the same expression and changes no runtime byte of it.
19 changes: 19 additions & 0 deletions .changeset/7122-postcss-dedupe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
---

Dev-time only: collapse the `postcss` duplicate this branch's first lockfile
bump introduced (objectui#7122).

`aabc527cb` resolved `@objectstack/spec` to 17.3.0 and, as a side effect of that
install, re-resolved `autoprefixer`'s auto-installed `postcss` peer to 8.5.28
while `@tailwindcss/postcss` kept 8.5.26. `@object-ui/cli` depends on both and
declares `postcss` itself, so its type-check program held TWO postcss copies
with two structurally distinct `Plugin` types — and comparing
`Plugin & ExportedAPI` against `Plugin` across them exceeded the compiler's
instantiation budget: `TS2321: Excessive stack depth`, in a file this branch
never touched. `origin/main` carries one copy for that pair and is green.

The split is resolved in the lockfile only. All eight manifests that declare
`postcss` already declare `^8.5.26`, which admits 8.5.28, so no declared range
moved and no published dependency declaration differs by a byte. Empty
frontmatter is the deliberate "no release" declaration.
39 changes: 39 additions & 0 deletions .changeset/7122-spec-17-3-0-parity-reconciliation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
'@object-ui/types': patch
'@object-ui/components': patch
'@object-ui/cli': patch
'@object-ui/plugin-detail': patch
---

Reconcile the declared surface with `@objectstack/spec` 17.3.0 (objectui#7122).

**`ObjectSchema.editMode` is now the spec's.** 17.3.0 adopted the key (measured:
the accept set went 42 → 43, gained set exactly `['editMode']`, lost set empty,
declared as the same `'page' | 'modal'` union objectui carried). Its local copy
is retired from `ObjectSchemaClientExtensions`, which is what that type's own pin
prescribed for this event, leaving the client delta empty. Nothing is removed
from the product: `editMode` stays authorable and stays typed on
`ObjectSchemaMetadata`, carried by the spec's `ServiceObject` instead of by a
local member — and a published, spec-validated object document may now carry it,
which at 17.2.0 was refused by name.

**`user:profile` is retired across all three sites.** 17.3.0 dropped it from
`PageComponentType` (measured: the enum went 34 → 32 options, lost set exactly
`['user:profile', 'element:form']`, gained set empty). objectui went on knowing
it in three places, so all three moved together: the Studio palette exclusion
ledger, `PROTOCOL_COMPONENTS` in `renderers/placeholders.tsx`, and the
regenerated `known-schema-types.ts` the CLI checks schemas against. Nothing
user-reachable went with it — neither type had a renderer, `user:profile` had
only the dashed "Component Placeholder" scaffold, and the app shell's own
profile affordance is a React slot, never this block type. A page schema still
naming it now draws the loud "Unknown component type" panel rather than a silent
grey box, which is this repo's standing treatment for a type outside the
supported surface.

**`record:details` sections document the eight keys 17.3.0 added.**
`group`, `hideEmpty`, `collapsible`, `showBorder`, `defaultCollapsed`, `icon`,
`description` and `headerColor` are now declared on a section entry (4 → 12
members). Six of the eight are already honoured by `DetailSection`, so the
`sections` input description now teaches all of them, and says plainly which two
are not read here. Designer controls for them are a separate feature and are
deliberately not added.
32 changes: 32 additions & 0 deletions .changeset/7122-spec-17-3-0-pin-repointing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
---

Re-point the pins `@objectstack/spec` 17.3.0 moved, and harvest three
exemptions the contract outgrew (objectui#7122). Test only; no package is
released by this change.

- **The `#7496` family, 14 assertions across three files.** 17.3.0 kept every
`submitBehavior.url` refusal and its reasoning and only restated provenance as
`(ruled 2026-08-11)`. The pins asserted the citation FORM, not the citation —
objectui#7702's defect shape — so they now assert the durable property via a
documented `CITES_ITS_RULING`, which still discriminates the spec's prose from
a locally hand-written sentence. Deliberately not re-pinned to the new prose
verbatim, which would move the brittleness rather than remove it.
- **`object-grid.defaultSort` became an ADR-0087 D2 tombstone**, so its
unpublished-key exemption, its GA-pending entry and its carve-out row are
deleted — the ninth harvest by the mechanism the eight before it were designed
to die by. The block's prose claiming a tombstone would NOT make an entry
stale is corrected; its own cited counter-example was harvested that way.
- **`OFF_SPEC_ARM_EXEMPTIONS` is now empty.** `element:number.filter` accepts
the array form (objectui#6206) and `object-grid.data` accepts the
discriminated `ViewDataSchema` object (objectui#6207) — both converged
upstream in the direction their own reasons named, with no declaration edited.
Both issues are closable as resolved-upstream.
- **Two negative pins re-derived, not inverted.** The `object-master-detail-form`
`formType` boundary now refuses what it used to admit (`invalid_value`, plus a
bespoke ADR-0001 prescription for `wizard`), and the location value schema is
now strict (`unrecognized_keys` naming the retired `latitude`/`longitude`
pair). Each pin records the new fact, keeps a control opposite it, and pins the
refusal's SHAPE. The `LocationField` fence explicitly re-derives why its
key-level assertions stay: no parse stands on the emit path, so strictness
changed the consequence of a spread regression, not the widget's exposure.
25 changes: 25 additions & 0 deletions .changeset/7122-spec-17-3-0-reconciliation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
'@object-ui/app-shell': patch
'@object-ui/auth': patch
---

Reconcile `@object-ui/app-shell` with `@objectstack/spec` 17.3.0 (objectui#7122).

`SchemaDiffEntryKind` gained an `unreachable` member in 17.3.0, and the external
datasource validation panel labels those kinds through a map that is TOTAL over
the union on purpose — so an upstream addition fails the build rather than
rendering a blank cell. That mechanism fired: the package did not compile against
17.3.0 until the kind was labelled. It now reads "Not checked — remote
unreachable", following the spec's own ruling that this kind asserts nothing
about the remote schema and must never be surfaced as "schema changed": it means
introspection could not complete, which is often transient, and labelling it like
a mismatch would tell an operator to repair a schema nobody has read.

Two internal flow-inspector types were renamed (`FlowNodeLike` →
`InspectorFlowNode` / `ScopeFlowNode`) because 17.3.0 began exporting its own
`FlowNodeLike`; neither name is in this package's published entry, so no consumer
import changes.

`@object-ui/auth`: a README sentence claiming the preview-mode prop aligns with
the spec's `PreviewModeConfig` is corrected — 17.3.0 removed that symbol. The
`previewMode` prop itself is host-supplied, unchanged, and unaffected.
8 changes: 8 additions & 0 deletions .changeset/spec-17-3-0-lockfile-bump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
---

Dev-time only: move the `@objectstack/spec` resolution in `pnpm-lock.yaml` from
17.2.0 to 17.3.0. No published package changes — every manifest's declared range
(`^17.0.0` / `^17.1.0` / `^17.2.0`) already admitted 17.3.0, so no floor moved and
no package's dependency declaration differs by a byte. Empty frontmatter is the
deliberate "no release" declaration for a change that publishes nothing.
51 changes: 44 additions & 7 deletions apps/console/src/__tests__/masterDetailFormTypeManifest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,52 @@ describe('object-master-detail-form.formType — the manifest carries the closed
},
);

it('⚠️ does NOT make the value rejected at the publish boundary (objectui#5155)', async () => {
// The other half of the honest reading. objectstack owns this schema; the
// objectui enum cannot narrow it, and a reader who stopped at the test above
// would conclude the value is now impossible to publish. It is not.
it('AND the publish boundary rejects it too, since spec 17.3.0 (objectui#5155)', async () => {
// The other half of the honest reading — and it REVERSED at
// `@objectstack/spec` 17.3.0, which is why this test now says the opposite
// of what it used to.
//
// It was written as a negative pin: objectstack owns this schema, the
// objectui enum cannot narrow it, and a reader who stopped at the manifest
// assertions above would wrongly conclude the value was impossible to
// publish. That was true and worth pinning while the boundary accepted
// `formType: 'wizzard'`. 17.3.0 closed the gap deliberately — not a
// silent narrowing: the schema answers `invalid_value` naming the two
// honoured options, and for the near-miss `'wizard'` it carries a bespoke
// prescription citing ADR-0001 and the renderer measurement behind it
// ("only the current wizard step's fields mount … so parent + details never
// save through the atomic batch").
//
// ⛔ The premise did not merely evaporate, so this is not an inverted
// assertion standing where a negative control used to. What the test owes
// its reader is the RELATIONSHIP between the two authorities, and that is
// what is pinned: the manifest diagnostic above and the publish boundary
// here now agree, and each is checked on its own so a future divergence
// fails HERE rather than reaching an author as a value that lints clean and
// then cannot be published.
const spec: any = await import('@objectstack/spec/ui');
const schema = spec.ObjectMasterDetailFormPropsSchema;
expect(schema, 'ObjectMasterDetailFormPropsSchema is not exported').toBeDefined();
expect(schema.safeParse({ objectName: 'po', details: [], formType: 'wizzard' }).success).toBe(
true,
);

// Positive control first: the boundary still admits the honoured pair, so a
// wholesale schema breakage cannot read as "the refusal works".
for (const honoured of ['simple', 'tabbed']) {
expect(
schema.safeParse({ objectName: 'po', details: [], formType: honoured }).success,
`the boundary stopped accepting the honoured value ${honoured}`,
).toBe(true);
}

const refused = schema.safeParse({ objectName: 'po', details: [], formType: 'wizzard' });
expect(refused.success).toBe(false);
if (refused.success) return;

// The SHAPE of the refusal, not just its existence: a value refusal at the
// key. `unrecognized_keys` would mean the key stopped being declared, and a
// `custom` refinement would mean it is refused by a cross-field rule rather
// than by its own vocabulary — three different facts, and only one of them
// is "the enum closed".
const own = refused.error.issues.filter((i: any) => i.path.join('.') === 'formType');
expect(own.map((i: any) => i.code)).toEqual(['invalid_value']);
});
});
Loading
Loading