Skip to content

Commit f784118

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-24916-approval-flow-i18n-coverage
2 parents 0a4800d + 2aaee3b commit f784118

41 files changed

Lines changed: 2942 additions & 464 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@objectstack/service-automation': patch
3+
'@objectstack/runtime': patch
4+
---
5+
6+
fix(service-automation,runtime): a flow whose data node is refused answers its caller with the refusal structured — `details.innerRefusal` and the producer-marked `userMessage` on every door's `400 FLOW_FAILED` — beside an unchanged `message`
7+
8+
Clause-②: yes (widening)
9+
Metadata-protocol: none
10+
11+
Ruling B′ on #24914 (the maintainer's 「同意」, decision batch #379 item 5), the emission half of the contract `@objectstack/spec` declared in #24996. A record save the platform refuses reaches a record form as `code`, `message` and the producer-marked `userMessage`; the same refusal met by a flow's data node used to be flattened into one string (`Node 'ID' failed: create_record(OBJECT) failed: …`), so a screen flow could only show its user that developer text.
12+
13+
- **`service-automation`.** The catch arms of `get_record`, `create_record`, `update_record` and `delete_record` read the refusal off the thrown error: the producer-marked `userMessage` (through `declaredUserMessage`) and, for a throw that declared a 4xx answer, `{ code, message }` with `code` in the WIRE spelling the data door answers for the same throw — `UNIQUE_VIOLATION` for a unique refusal, `VALIDATION_FAILED`, `PERMISSION_DENIED`, or the member an app hook's declared status names. `NodeExecutionResult` gains the two optional members, and the engine carries them from the failing node to `AutomationResult.userMessage` / `.innerRefusal` on `execute()`'s failure exit, the `resumeInternal` stranded exit and the retry exits. A fault (no declared answer, or a declared 5xx) carries neither member; nothing is read from message text.
14+
- **`runtime`.** The trigger, resume, `/actions` and declared-endpoint doors build their `400 FLOW_FAILED` details through one builder typed as `FlowFailureDetailsSchema` (`errorMessage?`, `summary?`, `innerRefusal?`) and put the run's `userMessage` at the envelope's top level (`ApiErrorSchema.userMessage`). The MCP `resume_run` bridge carries both as the REST resume door serves them. `DomainHandlerDeps.error` gains an optional fourth argument, `extra: { userMessage? }`, the declared slot the doors use; a call without it builds the envelope it always built.
15+
- **Unchanged.** Every `error` / `message` text, byte for byte — the node's, the engine's prefix, the stored run, approvals and each door's wording. The in-flow `$error.code` keeps the engine's in-process `DUPLICATE_RECORD`. `error.code` stays `FLOW_FAILED`, and `AutomationResult.code` stays the closed union.
16+
17+
**For consumers.** A flow runner can render a refused run the way a record form renders a refused save: the top-level `userMessage` when present, then `details.innerRefusal`, then `message`. A client that does not read the new members keeps its current behaviour. A container that rewords a child's failure into its own (`try_catch` without a catch region, `subflow`) does not forward the members yet; the run then carries none.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
'@objectstack/plugin-auth': patch
3+
'@objectstack/plugin-email': patch
4+
---
5+
6+
feat(plugin-auth, plugin-email): under the `open` and `email_domain` postures, a sign-up for an address that already has an account answers like a new sign-up and mails that address, while mail is really delivered
7+
8+
Clause-②: yes
9+
Metadata-protocol: none
10+
11+
The public email-and-password sign-up changes its answer in one case. Under the `open` and `email_domain` audience postures, a sign-up for an address that already has an account was refused explicitly. Now, when the deployment's mail transport really delivers, it answers exactly as a successful sign-up does on that deployment, and the address's holder receives a mail saying the address already has an account, with a link to sign in and a link to reset the password. Nothing is written: no new user, credential or session. The caller can no longer tell from the answer whether the address was registered.
12+
13+
- **When the explicit refusal stays, unchanged.**
14+
- The mail would not be delivered: the email service says its transport is not live (`IEmailService.isConfigured()` answers `false`, which plugin-email answers for its `log` transport in every environment), the email service does not implement `isConfigured()`, or no email service is wired. A success with no mail behind it is what the refusal replaced, so it is never answered.
15+
- The posture is `invite_only`. A stranger still learns nothing there, and an invited person whose address already has an account is still refused.
16+
- The posture is `open` with email verification declared off (and auto sign-in left on). A new sign-up there is signed in at once, and an existing address cannot get that answer without a session for someone else's account.
17+
- **Read at sign-up time.** The transport is asked on every sign-up, so a transport saved from the Mail settings applies to the next sign-up without a restart.
18+
- **The mail.** plugin-email ships a new built-in template, `auth.existing_account_sign_up`, in `en-US`, `zh-CN`, `ja-JP` and `es-ES`. It is registered like the other built-in auth templates, so an app declaration or a Studio save overrides it. Its variables are `user.name` (optional), `user.email`, `signInUrl`, `resetPasswordUrl` and `appName` (optional). The two links are the Console's sign-in and forgot-password pages and carry no token. `BUILTIN_AUTH_TEMPLATES` grows from 28 rows to 32.
19+
- **The mail's language.** The recipient's stored language (`sys_user.locale`), then the deployment default. The language of the browser that asked is never used, because the person signing up may not be the address's holder.
20+
- **For a client of the sign-up door.** In the configurations above, a screen that showed an "already registered" message on the refusal now receives the success answer instead, and should show its usual "check your email" step.
21+
- **A send that fails after the answer.** The answer is still the success, and the server logs one error-level line saying the mail was not sent. A verification mail that fails for a new address leaves the same gap.
22+
- **Unchanged.** A sign-up for a new address, every refusal the audience posture raises, and every other auth mail.

‎.changeset/25097-cloud-connection-platform-managed.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ fix(cloud-connection)!: a runtime holding the environment service key is platfor
66

77
A runtime whose control-plane credential is the environment service key (`controlPlaneApiKey`, or `OS_CLOUD_API_KEY`, which the cloud sets on the environments it hosts) is connected by the platform, not by anyone on the runtime. It still registered the Cloud Connection page in Setup's Apps group, which read "Connected" and offered Disconnect. Disconnecting there cleared the runtime's stored device credential while the service key stayed in use, so the page went on reading "Connected", and the control plane was asked to revoke a connection the platform had made.
88

9-
Such a runtime now registers neither the page nor its navigation entry, and says so once at boot. A request to disconnect it is refused with `409` and `RESOURCE_CONFLICT`, after the sign-in and Setup Access checks and before anything is revoked or cleared. The message says the platform manages the connection.
9+
Such a runtime now registers neither the page nor its navigation entry, and says so once at boot. A request to disconnect it is refused with `409` and `RESOURCE_CONFLICT`, after the sign-in and capability checks and before anything is revoked or cleared. The message says the platform manages the connection.
1010

11-
A self-hosted or local runtime, connected through the device-code flow, keeps the page and its disconnect unchanged. Its Cloud Connection entry moves from the Apps group to the Integrations group, after Webhooks, Datasources and Connect an Agent. That group requires `manage_platform_settings`, so a user who holds Setup Access without it, such as an organization administrator, no longer sees the entry. The connect and disconnect requests still take Setup Access alone.
11+
A self-hosted or local runtime, connected through the device-code flow, keeps the page and its disconnect unchanged. Its Cloud Connection entry moves from the Apps group to the Integrations group, after Webhooks, Datasources and Connect an Agent. That group requires `manage_platform_settings`, so a user who holds Setup Access without it, such as an organization administrator, no longer sees the entry.
1212

1313
Clause-②: no (narrowing)
1414

1515
<!-- adr-0087: not-required (no-migration-prescription) No metadata moves: no spec key, authorable spelling, object definition or stored shape is added, removed, renamed or re-shaped, and no exported declaration is removed or narrowed, so there is nothing for `objectstack migrate meta` to rewrite. What changes is what a runtime holding the environment service key serves: it no longer registers the Cloud Connection page or its navigation entry, and it refuses a disconnect it used to carry out. The navigation entry's new group is a value inside an exported constant whose type is unchanged. The other categories are closed on facts: the package publishes (not unpublished); no ADR-0087 id is named or touched (not registered or already-registered); and nothing exported is removed or narrowed (not runtime-interface-only, type-surface-only or runtime-export-retired). -->
1616

1717
**BREAKING** (on a runtime holding the environment service key, a disconnect request it carried out is now refused, and the Cloud Connection page and its navigation entry are no longer served), shipped as `minor` under the launch-window convention for breaking changes.
1818

19-
- **Unchanged:** a runtime without the service key, apart from the entry's group. The connection status answer is unchanged on every runtime.
19+
- **Unchanged:** a runtime without the service key, apart from the entry's group and the permission its connect and disconnect requests and its connection details require, which another change in this release raises to `manage_platform_settings`. The connection status answer is otherwise unchanged on every runtime.
2020
- **Nothing to rewrite:** a cloud-hosted environment's connection is the platform's to manage. A self-hosted server leaves `OS_CLOUD_API_KEY` unset, as the deployment guide says; one that sets it is platform-managed by this rule.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
'@objectstack/cloud-connection': patch
3+
---
4+
5+
fix(cloud-connection)!: one rule for the Cloud Connection capability — the page's record and its three connection-changing requests require `manage_platform_settings`, like its Setup entry; a server holding the environment service key also refuses `bind/start` and `bind/poll`
6+
7+
Clause-②: no (narrowing)
8+
Metadata-protocol: none
9+
10+
<!-- adr-0087: not-required (no-migration-prescription) No metadata moves: no spec key, authorable spelling, object definition or stored shape is added, removed, renamed or re-shaped, and no exported declaration is removed or narrowed, so there is nothing for `objectstack migrate meta` to rewrite. What changes is which signed-in callers the Cloud Connection routes serve: the capability they read moves from `setup.access` to `manage_platform_settings`, and a server holding the environment service key refuses two more of them. The navigation entry's added `requiredPermissions` is a value inside an exported constant whose type is unchanged. -->
11+
12+
**BREAKING** (a signed-in user who holds Setup Access, `setup.access`, without Manage Platform Settings, `manage_platform_settings`, such as an organization administrator under the default permission sets, can no longer connect or disconnect the server, and is no longer served the connection's details), shipped as `patch`: the change is to which callers the routes serve, not to the metadata protocol.
13+
14+
The Cloud Connection page's Setup entry sits in the Integrations group, which requires `manage_platform_settings`. The page's routes still took `setup.access` alone, so an organization administrator, whose default permission set carries Setup Access but not Manage Platform Settings, was not shown the entry but could open the page by its address and connect or disconnect the server. One rule now covers the entry, the page and the routes:
15+
16+
- **Refused now:** `POST /api/v1/cloud-connection/bind/start`, `POST /api/v1/cloud-connection/bind/poll` and `POST /api/v1/cloud-connection/unbind` answer a signed-in user who does not hold `manage_platform_settings` with `403` and the standard `PERMISSION_DENIED` code, and the message names that capability. Holding `setup.access` is no longer enough. The refusal still comes before any call to the cloud and before the stored credential is touched.
17+
- **Narrowed now:** `GET /api/v1/cloud-connection/status` serves `environmentId` and `connection` only to a user who holds `manage_platform_settings`. Every other caller, an organization administrator included, is served `bound`, `runtimeId` and `provider`, as an anonymous caller is.
18+
- **Declared on the entry:** the Cloud Connection navigation entry now declares `requiredPermissions: ['manage_platform_settings']` itself, the capability its group already required, so the entry and the routes read one rule from this package. The page carries no permission key of its own (a page has none); what it shows is served by these routes.
19+
- **Refused on a platform-managed server:** a server holding the environment service key (`controlPlaneApiKey`, or `OS_CLOUD_API_KEY`) already refused `unbind` with `409` and `RESOURCE_CONFLICT`. It now refuses `bind/start` and `bind/poll` the same way, after the sign-in and capability checks and before any call to the cloud or any write to the stored credential. A device connection completed there used to store a credential the service key always overrode, while presenting that key to the cloud, which could move the environment's cloud-side connection record. The message now says the connection cannot be connected or disconnected here, and that nothing was sent to the cloud and nothing was stored or cleared.
20+
- **Unchanged:** a user who holds `manage_platform_settings`, which the default platform-administrator permission set carries, is served exactly as before on a server without the service key. Anonymous callers still get `401`, and a session the authentication policy is holding still gets its `403`. The device-code flow's steps, the status answer for a permitted caller, and the four other session routes (`install`, `installation`, `installed`, `org-packages`) are unchanged.
21+
22+
**Who acts:** a deployment that relies on organization administrators, or another set holding only Setup Access, to connect its server grants `manage_platform_settings` to those users, through a permission set or a position, and they are served as before.

0 commit comments

Comments
 (0)