Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
41 changes: 41 additions & 0 deletions .changeset/action-override-notice-declaration-5611.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
---
'@object-ui/core': minor
'@object-ui/app-shell': minor
---

`ActionDef` now declares `overrideNotice?: string`, and both param-collection
handlers narrow their `action?: any` parameter to `ActionDef` (objectui#5611).

`overrideNotice` was produced, read, and declared nowhere. `DeclaredActionsBar`
composes the dispatch as `any` and hands it to the runner through a
`dispatch as ActionDef` cast; `useConsoleActionRuntime`'s param-collection
handler took `action?: any`. The key crossed the entire producer/reader seam
without a single declaration — so `warnOnUnknownActionKeys` told the author, in
dev, that a key "no reader recognizes" was present, on every privileged-override
dispatch, about a key two files actually read. That warning now stops, which is
the user-visible half: this restores an invariant rather than adding a feature.

Nothing new is accepted at runtime. The key already reached the runner through
that cast; declaring it only makes the type layer say what already happens. It
is documented at the declaration as objectui dialect with no spec counterpart —
`@objectstack/spec`'s `ActionSchema` has no such field, and unlike `description`
it has no authorable twin in `@object-ui/types` either, because a host composes
it in code. objectui#5178's ruling that it must NOT be folded into `description`
is restated there: the reader resolves `description` through
`_actions.<name>.description` and prefers a bundle hit over the passed literal,
and `plugin-approvals` ships exactly such an entry for `approval_reject`, so a
safety notice routed through `description` would be silently replaced by
ordinary copy in every locale that has the bundle.

The declaration and its `ACTION_DEF_KEYS` entry move together because
`actionKeys.pin.test.ts` re-derives that list from the interface's AST — measured
here by ablating the inventory entry alone, which fails the pin by name
(`missing: ['overrideNotice']`).

With the key declared, narrowing `useConsoleActionRuntime.tsx:196` and
`RecordDetailView.tsx:500` is a one-token change to each annotation — `ActionDef`
was already imported by both files. This is what puts both handlers under the
compiler at all; every other read in them was already a declared field.
objectui#4282 attempted the same narrowing, hit three `TS2339`s on this key, and
backed out rather than casting at the use site. Ablating the declaration and
rebuilding restores exactly those three diagnostics.
2 changes: 1 addition & 1 deletion packages/app-shell/src/hooks/useConsoleActionRuntime.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -193,7 +193,7 @@ export function useConsoleActionRuntime(opts: ConsoleActionRuntimeOptions): Cons
});
}, []);

const paramCollectionHandler = useCallback<ParamCollectionHandler>((params: ActionParamDef[], action?: any) => {
const paramCollectionHandler = useCallback<ParamCollectionHandler>((params: ActionParamDef[], action?: ActionDef) => {
return new Promise<Record<string, any> | null>((resolve) => {
// List_item actions stash the row record under params._rowRecord (see
// ObjectGrid → onRowAction). Pull it out so resolveActionParams can
Expand Down
2 changes: 1 addition & 1 deletion packages/app-shell/src/views/RecordDetailView.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -497,7 +497,7 @@ export function RecordDetailView({ dataSource, objects, onEdit, objectNameOverri
});
}, []);

const paramCollectionHandler = useCallback((params: ActionParamDef[], action?: any) => {
const paramCollectionHandler = useCallback((params: ActionParamDef[], action?: ActionDef) => {
return new Promise<Record<string, any> | null>((resolve) => {
// Related-list row actions retarget a CHILD object (e.g. sys_member rows
// on an org record page) and stash the clicked row under
Expand Down
32 changes: 32 additions & 0 deletions packages/core/src/actions/ActionRunner.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -142,6 +142,38 @@ export interface ActionDef {
* it — the two TS2353s the deletion produced repo-wide were both this key.
*/
description?: UIActionSchema['description'];
/**
* A notice that must reach the user AHEAD of the declared description, shown
* at the top of the param-collection dialog's subtitle.
*
* objectui dialect with NO spec counterpart, and dialect in a stronger sense
* than {@link ActionDef.description}: that one at least derives from
* `@object-ui/types`' renderer view of an action, while this key is not
* authorable metadata at all. A HOST composes it in code — `DeclaredActionsBar`
* sets it when the viewer takes the privileged admin-override branch
* (`can_act:false && can_override:true`), naming the approvers about to be
* bypassed. Hand-typed `string` rather than derived precisely because there is
* no spec field to derive FROM. The notice arrives already localized (bar
* chrome resolved through the normal locale bundle), so its reader
* concatenates it verbatim.
*
* Deliberately NOT folded into `description` (objectui#5178): the reader
* resolves `description` through `_actions.<name>.description` and PREFERS a
* bundle hit over the passed literal, and `plugin-approvals` ships exactly
* such an entry for `approval_reject` — so a warning routed through
* `description` would be silently replaced by the ordinary "Reject this
* request?" copy in every locale that has the bundle. A safety notice a
* translation can delete is not a safety notice.
*
* Promoted for the same reason step 3 (objectstack#4075) promoted
* `description`: produced, read, and declared nowhere. Its producer composes
* the dispatch as `any` and hands it over through a `dispatch as ActionDef`
* cast, and its reader took `action?: any` — so the key crossed the whole
* seam without one declaration, and `warnOnUnknownActionKeys` told the author
* in dev that a key "no reader recognizes" was present, on every
* privileged-override dispatch, about a key two files read.
*/
overrideNotice?: string;
/**
* Confirmation text — shows a confirm dialog before executing. The ONE
* confirm spelling: `@objectstack/spec`'s action surface and the
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/actions/actionKeys.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -121,6 +121,12 @@ export const ACTION_DEF_KEYS = [
// of an action), forwarded by all four action renderers and read by the
// param-collection dialog, but never declared on `ActionDef`.
'description',
// objectui dialect with no spec counterpart and no authorable twin — a HOST
// (`DeclaredActionsBar`) composes it in code for the privileged
// admin-override branch, and the param-collection dialog reads it ahead of
// `description`. Listed here so the dev-mode warning stops reporting a key
// two files read as one "no reader recognizes".
'overrideNotice',
'confirmText',
'confirm',
'condition',
Expand Down
Loading