Skip to content
Merged
34 changes: 34 additions & 0 deletions .changeset/11016-cross-repo-bare-citations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
'@object-ui/core': patch
'@object-ui/app-shell': patch
---

Citations of objectstack cards now name their repository (objectui#11016).

A bare `#N` in this repository resolves to an objectui item. Where the sentence
meant an objectstack card, a reader who followed the number landed on an
unrelated objectui card or pull request, and nothing flagged it: both
repositories answer for the number, so an earlier sweep that looked for dead
numbers could not see this class.

Two of those citations are runtime text, and they now name the card they mean:

- `@object-ui/core`: the dev warning for an action that still carries the
retired `execute` key (`RETIRED_ACTION_KEYS.execute`) points at
objectstack-ai/objectstack#3855, the card that retired the alias. The rest of
the message, including "rename the key to `target`", is unchanged.
- `@object-ui/app-shell`: the Studio skill form's help text for
`triggerConditions` points at objectstack-ai/objectstack#1878 and
objectstack-ai/objectstack#1895.

The other sites are code comments and doc comments in the non-test sources,
requalified the same way: a bare number whose sentence meant
an objectstack card now reads `objectstack-ai/objectstack#N`. Numbers that mean
an objectui card stay bare. Where a comment quotes objectstack's own text, the
quote keeps its words and the card name sits in square brackets, the editorial
mark for a substituted word.

Pending entries in this release that carry such a number get a dated note
naming the card, and their frontmatter is unchanged.

Nothing else moves: no key, accept set, refusal decision or code path.
5 changes: 5 additions & 0 deletions .changeset/4664-related-list-declared-filter.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ related lists answered wider than the metadata asked, and the driving scenario
soft-deleted child rows on auto-derived record pages — had no way to be
expressed at all.

⚠️ **Dated note, 2026-09-29 — the pull request beside objectstack#8704 is objectstack's — objectui#11016.** The spec key's
pull request is named above by a bare number, which in this repository resolves
to an unrelated objectui pull request. It is objectstack-ai/objectstack#8955, the
spec change that declared the field-level `relatedListFilter`. The text above is kept as the reading of this change.

What ships:

- `deriveRelatedLists` reads the key off the FK and carries it on the derived
Expand Down
5 changes: 5 additions & 0 deletions .changeset/6956-listview-export-options-spec-mirror.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ intact. The mirror now IS that schema object, so the four verdicts are the
spec's by construction; `export-options-spec-parity.test.ts` pins the identity,
the four verdicts, and the survival of `streaming` through a parse.

⚠️ **Dated note, 2026-09-29 — the card behind the mirror's old shape is objectstack's — objectui#11016.** The shape the
mirror used to declare is dated above by a bare number, which in this repository
resolves to an unrelated objectui item. It is objectstack-ai/objectstack#8010, the
card the first paragraph cites as objectstack#8010. The text above is kept as the reading of this change.

**The TS face follows.** `ListViewSchema['exportOptions']` is now the spec's
INPUT type: `ListViewExportFormat[] | ListViewExportOptions` — the bare array
stays admissible on input because nothing on the render path parses, and the
Expand Down
7 changes: 7 additions & 0 deletions .changeset/7122-spec-17-3-0-pin-repointing.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,10 @@ released by this change.
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.

⚠️ **Dated note, 2026-09-29 — the ruling number in the first two bullets is objectstack's — objectui#11016.** The first
two bullets name the `submitBehavior.url` ruling by a bare number, once as the
family's name and once inside the quoted 17.2.0 spelling. In this repository that
number resolves to an unrelated objectui item; it means
objectstack-ai/objectstack#7496, the spec card that ruled the semantics. The quoted
spelling is the artifact's own and stays verbatim. The text above is kept as the reading of this change.
6 changes: 6 additions & 0 deletions .changeset/9137-dataset-drill-conjoin-widget-filter.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ multi-condition dataset drill — including paths pinned by objectui#9085,
objectui#4056, objectstack#5473 and #1752 — for no change in the rows selected
or the `filter[...]` params emitted.

⚠️ **Dated note, 2026-09-29 — the last pinned path in the list above is objectstack's — objectui#11016.** The list of
pinned paths above ends with a bare number after objectstack#5473, which in this
repository resolves to an unrelated objectui pull request. It is
objectstack-ai/objectstack#1752, the card that made a time-bucketed date dimension
drill by range rather than by equality. The text above is kept as the reading of this change.

**The failure direction, corrected by measurement.** This was filed and graded
as a silent fail-OPEN superset, on the premise that an index key is one "nothing
reads as a condition". It is not: a bare array is not a legal equality comparand
Expand Down
5 changes: 5 additions & 0 deletions .changeset/dashboard-aria-member-retired-5830.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ claimed alignment with `@objectstack/spec AriaPropsSchema`, but the spec removed
a tombstone that refuses any value and tells authors to delete the key — and no
dashboard renderer ever read `schema.aria` (objectui#5830).

⚠️ **Dated note, 2026-09-29 — the audit close-out named above is objectstack's — objectui#11016.** The audit close-out
is cited above by a bare number, which in this repository resolves to an unrelated
objectui card. It is objectstack-ai/objectstack#3896, whose audit close-out is where
the spec retired `dashboard.aria`. The text above is kept as the reading of this change.

What an author loses is the **type-level suggestion** only: the key was already
refused at parse (the Zod twin inherits the spec tombstone by reference), and
`BaseSchema`'s index signature means an existing `aria:` line still compiles.
Expand Down
5 changes: 5 additions & 0 deletions .changeset/designer-publish-package-binding.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,8 @@ narrowing at the runtime publish gate fire on an HTTP-driven promotion at all. T
narrowing has a second, independent gate this does not touch — objects carrying no
`_packageId` provenance are kept unconditionally — so on a tenant-authored overlay
corpus stating the package still narrows nothing.

⚠️ **Dated note, 2026-09-29 — the publish-gate narrowing is an objectstack card — objectui#11016.** The last paragraph
names the package-closure narrowing by a bare number, which in this repository
resolves to an unrelated objectui pull request. It is
objectstack-ai/objectstack#9612, the runtime publish gate's rule snapshot. The text above is kept as the reading of this change.
6 changes: 6 additions & 0 deletions .changeset/gantt-navconfig-comment-once.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,10 @@
`width` default supplies the identical `min(960px, 60vw)`. The resolved
overlay width is unchanged on every viewport, and is now pinned by a test.

⚠️ **Dated note, 2026-09-29 — the card in the quoted deprecation tag is objectstack's — objectui#11016.** The second
bullet quotes the spec's deprecation tag for `width`, whose bare number resolves
in this repository to an unrelated objectui item. It is
objectstack-ai/objectstack#2578, the spec card behind the `size` bucket that
replaces `width`. The text above is kept as the reading of this change.

No published behaviour changes.
6 changes: 6 additions & 0 deletions .changeset/kanban-calendar-nav-width-converge.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ calendars take. With both omitted, `resolveOverlayWidth` returns `undefined` and
`RecordDetailDrawer`'s own `width` default supplies the identical
`min(960px, 60vw)`.

⚠️ **Dated note, 2026-09-29 — the card in the quoted deprecation tag is objectstack's — objectui#11016.** The paragraph
above quotes the spec's deprecation tag for `width`, whose bare number resolves
in this repository to an unrelated objectui item. It is
objectstack-ai/objectstack#2578, the spec card behind the `size` bucket that
replaces `width`. The text above is kept as the reading of this change.

The resolved overlay width is therefore unchanged on every viewport, for both a
board/calendar that declares no `navigation` and one that authors
`navigation.width` — each is now pinned by a test. The three renderers agree
Expand Down
6 changes: 6 additions & 0 deletions .changeset/render-publish-advisory-findings-5026.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ objectui#4133 / PR #4236 wired the gate's advisories to the save door and record

The second reason has expired. `PublishMetaItemResponseSchema` now declares the same optional, omitted-when-empty `advisories` key that `SaveMetaItemResponseSchema` has carried since #4717, and `publishMetaItem` populates it. Measured against the installed `@objectstack/spec` (17.2.0) rather than inferred from the version number: the key survives a `safeParse`, a half-shaped finding is rejected, and a clean publish omits the key entirely. That reading is now a test rather than a note, so a spec drift fails CI instead of silently re-muting the door.

⚠️ **Dated note, 2026-09-29 — the save response's advisories key came from an objectstack card — objectui#11016.** The
paragraph above dates the save response's `advisories` key by a bare number,
which in this repository resolves to an unrelated objectui item. It is
objectstack-ai/objectstack#4717, the card that put the runtime authoring gate's
advisory findings on the response. The text above is kept as the reading of this change.

`MetadataClient.publish` and `MetadataClient.publishDraft` — the two methods over the single-item publish route `POST /meta/:type/:name/publish` — now report through the **same** sink, the same event and the same renderer the save door already used. No new UI shape: same warning tier, same 10s duration, same per-finding `rule` + `message` + `hint` formatting, findings still rendered verbatim as server prose. The wiring lands in the data layer rather than at the call sites, so `ResourceEditPage`'s Publish button and the runtime `RuntimeDraftBar` promotion (ObjectView / ReportView / DashboardView) are covered by one change, as are future ones.

One thing had to differ, and it is the frame's verb. Save and Publish are two different buttons in this product, so a toast that says "Saved" after a Publish tells the author their change is still a draft — the opposite of what happened. `MetadataSaveAdvisoryEvent` therefore gains a required `door: 'save' | 'publish'` and the renderer picks `console.publishAdvisoryTitle` (added to all ten locale packs) accordingly. `door` exists because `mode` cannot answer this: a direct active save and a draft promotion both report `mode: 'publish'`, since both land the body in the active overlay. It is required rather than optional so a future third door cannot be wired without saying which one it is, and the renderer branches on it through an exhaustive switch with a `never` check, so adding a third member is a compile error rather than a silently wrong verb.
Expand Down
6 changes: 6 additions & 0 deletions .changeset/violet-donkeys-brake.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,10 @@ PackageFormDialog: a producer-marked `error.userMessage` now outranks the locali

Creating or saving a package refused with **409** (id taken) or **403** (no `manage_metadata` capability) answered with a localized constant and discarded the envelope's prose — including a `userMessage` the producer had marked, at throw time, as addressed to the person reading the screen (ADR-0112 / objectui#3821). Those are the two refusals an author meets most on this surface, so the one channel written for them was dropped exactly where it mattered.

⚠️ **Dated note, 2026-09-29 — the card cited beside ADR-0112 is objectstack's — objectui#11016.** The
paragraph above cites the generic-substitution card under this repository's name,
which here resolves to an unrelated objectui pull request (the check-i18n-en-drift
script). It is objectstack-ai/objectstack#3821, the sharing-rule dialog card, whose
objectui repair (objectui#2920) introduced the generic substitution. The text above is kept as the reading of this change.

Both arms now render a marked sentence when the envelope carries one, and keep the localized constant as the fallback for an **unmarked** body — which is every refusal the package doors emit today, so the settled localized posture for a withheld capability (objectstack#8270) is unchanged. This also removes a divergence that depended on the transport: the same marked body used to read one way when an HTTP status survived the wire and another way when it did not.
4 changes: 2 additions & 2 deletions packages/app-shell/src/chrome/CommandPalette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export function CommandPalette({ apps, activeApp, objects, onAppChange, dataSour
}, [objects]);

// Group the (server-ranked) record hits by object so the palette lists them
// under per-object headings — issue #3371 asks for record hits "grouped by
// under per-object headings — issue objectstack-ai/objectstack#3371 asks for record hits "grouped by
// object". The object with the top-ranked hit leads (first-seen order), and
// within each group the server's relevance order is preserved.
const recordGroups = useMemo(() => {
Expand Down Expand Up @@ -216,7 +216,7 @@ export function CommandPalette({ apps, activeApp, objects, onAppChange, dataSour
)}

{/* Record search hits from the platform's global search
(/api/v1/search), grouped by object (issue #3371). The searching
(/api/v1/search), grouped by object (issue objectstack-ai/objectstack#3371). The searching
pulse rides the first group's heading so it stays visible while a
refined query is in flight over an existing result set. */}
{recordGroups.map((group, groupIndex) => {
Expand Down
22 changes: 11 additions & 11 deletions packages/app-shell/src/console/AppContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ export function AppContent({ extraRoutes, extraRoutesNoApp }: AppContentProps =
// skips hidden apps to avoid landing the user on a personal-settings
// app by default.
//
// ⛔ `hidden` only — never `_unpublished` (objectstack#6955 / #4829 A1). The
// ⛔ `hidden` only — never `_unpublished` (objectstack#6955 / objectstack-ai/objectstack#4829 A1). The
// publish gate is enforced server-side; duplicating it here would strand a
// builder on their own unpublished app.
const activeApps = apps.filter((a: any) => a.active !== false);
Expand Down Expand Up @@ -395,7 +395,7 @@ export function AppContent({ extraRoutes, extraRoutesNoApp }: AppContentProps =
}
}, [activeApp?.name, appName, location.pathname, navigate]);

// #2604 — the create/edit overlay is URL-driven (`?form=new` / `?form=<id>`),
// objectstack-ai/objectstack#2604 — the create/edit overlay is URL-driven (`?form=new` / `?form=<id>`),
// not component state: the record form is a TASK overlay over the origin
// route, and putting its open-state in the URL makes browser Back close the
// overlay (returning to the intact origin) instead of abandoning the route
Expand All @@ -405,7 +405,7 @@ export function AppContent({ extraRoutes, extraRoutesNoApp }: AppContentProps =
// ahead in history.
const [searchParams, setSearchParams] = useSearchParams();
const recordFormParam = searchParams.get(RECORD_FORM_PARAM);
// #2604 D3 — child-task extension of the record-form URL contract:
// objectstack-ai/objectstack#2604 D3 — child-task extension of the record-form URL contract:
// `formObject` names the object the form edits when it is NOT the route's
// object (a subtable child opened over its parent's detail); `formLink`
// ("field:id") pre-links the parent on create. Keeping the whole task in
Expand Down Expand Up @@ -563,7 +563,7 @@ export function AppContent({ extraRoutes, extraRoutesNoApp }: AppContentProps =
const currentObjectDef = allObjects.find((o: any) => o.name === objectNameFromPath);

// The object the record-form overlay edits: the route's object by default,
// or the `formObject` child override (#2604 D3 — subtable child task opened
// or the `formObject` child override (objectstack-ai/objectstack#2604 D3 — subtable child task opened
// over its parent's detail).
const formObjectDef = formObjectParam
? allObjects.find((o: any) => o.name === formObjectParam)
Expand All @@ -586,15 +586,15 @@ export function AppContent({ extraRoutes, extraRoutesNoApp }: AppContentProps =
executeAction({ type: 'dialog_cancel' });
}, [executeAction]);

// #2604 save invariant — *edit never moves you; create takes you to the
// objectstack-ai/objectstack#2604 save invariant — *edit never moves you; create takes you to the
// record you made.* Edit save: the origin route is untouched (crud_success
// bumps refreshKey → origin refetches in place). Create save: land on the
// new record's detail, on ITS derived surface — a light object's detail is
// the drawer over the still-intact list; a heavy one is the detail route.
// `replace: true` swaps out the transient `?form=…` entry so Back returns
// to the pre-create origin.
const handleRecordFormSuccess = useCallback(async (saved: any) => {
// Child task (#2604 D3): the parent detail must stay EXACTLY as it was —
// Child task (objectstack-ai/objectstack#2604 D3): the parent detail must stay EXACTLY as it was —
// active tab, scroll, everything. So do NOT go through crud_success;
// the child's open related lists refetch on their own via the
// invalidation bus (#2269): the dataSource write already emitted a
Expand Down Expand Up @@ -1025,7 +1025,7 @@ export function AppContent({ extraRoutes, extraRoutesNoApp }: AppContentProps =
no organization), and widening it for filter resolution would couple
two unrelated contracts. Renderer packages deliberately do not depend
on @object-ui/auth, so the shell supplies the values (framework
#3574). */}
objectstack-ai/objectstack#3574). */}
<FilterScopeProvider
currentUserId={user?.id ?? null}
currentOrgId={activeOrganization?.id ?? null}
Expand Down Expand Up @@ -1146,10 +1146,10 @@ export function AppContent({ extraRoutes, extraRoutesNoApp }: AppContentProps =
objectName: formObjectDef.name,
mode: editingRecord ? 'edit' : 'create',
recordId: editingRecord?.id,
// Child create task (#2604 D3): pre-link the parent from the
// Child create task (objectstack-ai/objectstack#2604 D3): pre-link the parent from the
// `formLink` URL param (refresh-safe — the link survives).
...(formLinkValues && !editingRecord ? { initialValues: formLinkValues } : {}),
// #2604 D1: create/edit follow the flow-surface derivation —
// objectstack-ai/objectstack#2604 D1: create/edit follow the flow-surface derivation —
// field-heavy → full-screen modal (the same big canvas the
// detail page gets, with overlay return semantics); light
// objects keep the existing auto-sized modal (ModalForm
Expand All @@ -1170,7 +1170,7 @@ export function AppContent({ extraRoutes, extraRoutesNoApp }: AppContentProps =
// When the view declares sections they drive the modal layout and
// win over the flat `fields` list below; otherwise this resolves to
// {} and `fields` (every field, raw schema order) is used as before.
// `formType` stays 'modal' (the container). (#1890 / ADR-0050.)
// `formType` stays 'modal' (the container). (objectstack-ai/objectstack#1890 / ADR-0050.)
...resolveFormViewLayout(formObjectDef as any),
title: editingRecord
? t('form.editTitle', { object: objectLabel(formObjectDef as any) })
Expand Down Expand Up @@ -1255,7 +1255,7 @@ function buildItemRoute(item: any, ctx?: NavTemplateContext): string {
* the ROOT lands on is a separate question, decided by `isDefault`.
*
* This used to honour an explicit `app.homePageId`. Spec 17.0.0 retired that
* key (objectstack#4667, premise corrected in #4709 / objectui#3287): it was
* key (objectstack#4667, premise corrected in objectstack-ai/objectstack#4709 / objectui#3287): it was
* an ID cross-reference with no referential integrity, so a dangling id fell
* back to the first item *silently*. If the capability returns, the correct
* encoding is a flag on the navigation item itself (`navigation[].landing`),
Expand Down
Loading
Loading