Skip to content

console: execute the declarative row-level operation: 'update' action — dispatch on operation before type, forward operation / patch through the action forward lists, Studio inspector keys, Undo anchor (objectui half of objectstack#14092) #7551

Description

@zhuangjianguo

Blocked-by: objectstack-ai/objectstack#14092

Filed by the objectstack domain:spec seat (session_0174WZTU6XcFcS7g2kykC53i, seat post objectstack#6017) at the contract-review ACCEPT of objectstack PR #15077 (the spec half), per the contract-first split of the maintainer ruling on objectstack#14092 (ruling comment 5494341350, 2026-09-01, verbatim 「同意」). Filed with pm:blocked; the domain:* label is this repo's triage (landing packages packages/core + packages/app-shell, the domain:ui stream). Installability, not merge, unlocks it: this repo consumes @objectstack/spec as a published package (the seat's reading; the unlock scan verifies), so before dispatch the scan confirms a spec release carrying ActionSchema.operation / patch is installable here — a merged-but-unpublished spec means pm:on-hold with a Restart-when: on the install probe, not pm:queue. The runtime half is filed on objectstack as a sibling card; the two halves land in any order — until both land, an authored update action reaches POST /api/v1/actions/{object}/{action} on the script path and gets the route's loud refusal.

Spec shape this consumes (objectstack PR #15077, head 44e26d67)

ActionSchema gains operation: 'update' (one member, a parallel key beside type; type keeps its materialized default 'script') and patch (a record of static field values, optional). Refused beside operation: 'update': target, body, method, bodyExtra, bodyShape, recordIdParam, recordIdField, onSuccess, opensInNewTab, newTabUrl, list_toolbar in locations, and any explicit type other than 'script'. patch without operation and operation with neither patch nor params are refused. The parsed shape is always { type: 'script', operation: 'update', patch }. ActionType did NOT gain a member.

Executor contract (pinned in PR #15077's body; the console half)

  1. Read operation before type. ActionRunner's dispatch (packages/core/src/actions/ActionRunner.ts:1074, keyed on actionType into builtinExecutors, a Record over RunnableActionType, :863-867) branches on operation === 'update' first. Because ActionType did not move, that Record compiles unchanged on the spec bump.
  2. The write is performed by the platform action route (the runtime half): the console POSTs the route executeScript already posts (:1335; /api/v1/actions/{object}/{action}, :306) with the current recordId and the collected params. It does NOT perform a client-side dataSource.update — that would bypass the route's permission floor, hooks and validations. The route's refusal envelope (code + status) is surfaced, not swallowed into a green success toast (the objectui#2960 class).
  3. Forward operation / patch off the schema wherever bodyExtra is forwarded today — the action forward lists that scripts/check-action-forward-parity.mjs diffs (the objectui#4050 gate). Sites measured on origin/main 3e01cb55 (2026-09-03T20:32Z): packages/app-shell/src/hooks/useConsoleActionRuntime.tsx:397-475, packages/app-shell/src/views/RecordDetailView.tsx:679-804, plus every list the parity gate names.
  4. undoable: true: the result's prior values of exactly the written fields drive the Undo affordance the existing readers implement — the anchor the ruling names.
  5. visible with the record bound, fail-closed, as today — a UI gate, not authorization.
  6. Studio: the action inspector (packages/app-shell/src/views/metadata-admin/inspectors/ActionDefaultInspector.tsx; preview packages/app-shell/src/views/metadata-admin/previews/ActionPreview.tsx) exposes operation / patch as authorable keys with the spec's refusal set mirrored in the form (an author cannot pair patch with target).
  7. The packages/types mirror (UIActionSchema / RunnableActionType, imported at ActionRunner.ts:24) carries the two keys from the spec, not a hand-typed twin.

Pins

  • an operation: 'update' row action dispatches to the platform action route with recordId + params — never to the executeActionSchema no-op and never to a client-side update; the route's refusal (code + status) reaches the user; success refreshes per refreshAfter;
  • the forward-parity gate is green with the two new keys; list_toolbar placement is not offered (the spec refuses it; the bulk def is that bar's home);
  • undoable restores exactly the written fields from the result's prior values;
  • reverse pin: script actions without operation behave byte-for-byte as before.

Not this card

The runtime write (the objectstack sibling card); packages/spec (objectstack); the liveness ledger flip (the objectstack spec seat's card).

Refs

objectstack#14092 · objectstack PR #15077 · objectui#4050 (forward-list parity gate) · objectui#2960 (green toast on a no-op dispatch)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:blockedpriority:p2

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions