fix(app-shell)!: the server-action URL identifies an action by name, not target (ADR-0110 D1) - #2970
Merged
Merged
Conversation
…not target (ADR-0110 D1)
serverActionHandler posted `action.target || action.name` — the handler's
registration KEY — to /api/v1/actions/:object/:action. The server resolves
the declaration by NAME, so for a target-bound action it resolved none and
silently skipped both the ADR-0066 D4 capability gate and the ADR-0104 param
contract: a button correctly hidden from users without the capability posted
to an endpoint that accepted anyone (framework#3935).
target is a binding expression — a handler key here, a flow id for
type:'flow', a URL for type:'url', ${param.X}-interpolatable, and legally
non-unique — so it cannot identify a declaration. The URL now carries
action.name and the server derives the handler key from what it resolved.
An action with no name is refused rather than falling back to target.
apiHandler and flowHandler are unchanged: their target genuinely is the
endpoint / flow id they dispatch on.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DTTKgYriDB6RVrkYjxxnG1
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Resolves the serverActionHandler conflict: main routed the /actions response through the shared interpretActionResponse helper (objectstack#3913's envelope rule, single-sourced with RecordDetailView) in the same lines where ADR-0110 D1 renamed targetName -> actionName. Kept main's helper, passing the action NAME. 35/35 in useConsoleActionRuntime.test.tsx after the merge.
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The client half of ADR-0110 D1.
Ships in lockstep with objectstack-ai/objectstack#3958 — merge together. That PR teaches the server to derive the handler key from the declaration it resolves; this one stops posting the handler key as if it were the identity.
The defect
serverActionHandlerpostedaction.target || action.name— the handler's registration key — to/api/v1/actions/:object/:action. The server resolves the declaration by name, so for a target-bound action ({ name: 'complete_task', target: 'completeTask' }) it resolved no declaration and silently skipped both the ADR-0066 D4 capability gate and the ADR-0104 param contract.The user-visible shape of that: the Console correctly hides a button from users lacking its
requiredPermissions(it reads the declaration it holds), while the endpoint that same button posts to accepted anyone. That is precisely the dual-surface inversion D4 exists to prevent — see objectstack-ai/objectstack#3935.Why
nametargetis a binding expression, not an identity:script, a flow id forflow, a URL forurl, a FormView name forform;${param.X}/${ctx.X}-interpolatable, so it may not be a fixed string at all;nameisSnakeCaseIdentifierSchema— the machine identity. The public REST docs have always taught the name-addressed URL (curl .../actions/todo_task/complete_task); this brings the Console in line with the contract the rest of the platform already assumed.An action with no
nameis now refused outright rather than falling back totarget— silently reintroducing the bug is worse than a clear error.Unchanged on purpose
apiHandlerandflowHandlerkeep readingtarget: fortype: 'api'the target genuinely is the endpoint, and fortype: 'flow'it is the flow id posted to/api/v1/automation/:target/trigger. Only the/actions/...URL was conflating a binding with an identity.modalActionHandlerdelegates toserverActionHandler, so it is covered.Tests
Two new cases in
useConsoleActionRuntime.test.tsx— the URL carries the name and not the target for app-todo's real target-bound shape, and a nameless action is refused without a fetch. 31/31 pass in the file.🤖 Generated with Claude Code
https://claude.ai/code/session_01DTTKgYriDB6RVrkYjxxnG1
Generated by Claude Code