|
| 1 | +--- |
| 2 | +'@objectstack/service-automation': minor |
| 3 | +--- |
| 4 | + |
| 5 | +feat(service-automation)!: a `notify` node mails the address in an email field of the trigger record, and refuses a computed address no user holds |
| 6 | + |
| 7 | +Clause-②: no (narrowing: a recipient that the value expression of a `notify` node whose `channels` include `email` evaluates to an email address no user holds now fails the node at run time instead of being mailed) |
| 8 | +Metadata-protocol: narrowed |
| 9 | + |
| 10 | +A `notify` node can now reach a person who is not a user, such as an applicant who came in through a public form. `recipients: { emailField: 'applicant_email' }` with `channels: 'email'` (the form `@objectstack/spec` declared earlier in this release, which until now failed the run at the node) mails the address held in that `email`-typed field of the record that triggered the flow. |
| 11 | + |
| 12 | +- **The address.** It is read from the stored record by id when the node runs, never from the record snapshot the run carries. It must be exactly one plain ASCII mailbox, `name@example.com`. A list, a display name, a separator, a line break or an audience prefix such as `role:` fails the node and nothing is sent, and so does an address the `email` field type accepts but this node does not: a non-ASCII character, a quoted local part or an IP-literal domain. So do an empty field, a record that is not stored and a run with no trigger record. The failure names the field and the record, never the value. |
| 13 | +- **The send.** The title and message render as for any notify, the email goes through the deployment's outbound mail, and it is recorded like every notification: a `sys_notification` event and a `sys_notification_delivery` row whose recipient is the address. |
| 14 | +- **Per-address limit.** One address receives at most 5 email-field sends per hour (`EMAIL_FIELD_SENDS_PER_WINDOW`, `EMAIL_FIELD_SEND_WINDOW_SECONDS`), across every flow, counted case-insensitively in the kernel `cache` service (per process when none is registered, said once). A sixth fails the node before anything is written. The limit is best-effort: when the counter faults, the send is admitted and a warning is logged once. |
| 15 | +- **BREAKING (narrowed): a computed address the node mails must be a user's.** In a `notify` node whose `channels` include `email`, a recipient that a value expression (a CEL envelope) evaluates to and that reads as an email address, bare or as `user:ADDRESS`, now resolves to the user who holds it, and fails the node when no user holds it (or when no user directory can be read), with a message that points to `{ emailField }`. Before, such an address was mailed as it stood, so an expression over record text let whoever typed that text choose who the flow mailed. A node that does not name `email` (`inbox` alone, the messaging default) mails nobody and is unchanged. An address written out in the flow (`recipients: ['ops@example.com']`) is the author's constant and is sent as written, as before. |
| 16 | +- **Who moves.** A flow that mails (`channels` include `email`) a recipient its expression evaluates to an address no user holds. Measured over `examples/**` at `47af3dd`: no `notify` node names the `email` channel, so no in-repo flow changes. (Of the 17 `recipients` envelopes, the app-showcase ones over `record.owner` / `ownerId` do evaluate to addresses, since that field holds the owner's email, but those nodes are `inbox` only.) Fix: give the address its own `notify` node with `recipients: { emailField: 'FIELD' }` and `channels: 'email'`, or give the person a user. |
| 17 | +- **Dependency.** `@objectstack/plugin-auth` moves from a dev dependency to a dependency of `@objectstack/service-automation`: the limit counts through its `rate-limit-storage` fixed-window counter, the repository's one counter, as `@objectstack/service-sms` does. This is a new workspace edge, `service-automation` → `plugin-auth`, and no cycle: neither `plugin-auth` nor what it depends on (`service-messaging`, `rest`) depends on `service-automation`. |
| 18 | + |
| 19 | +<!-- adr-0087: not-required (no-migration-prescription) a run-time refusal in a `notify` node that mails (`channels` include `email`) of a recipient that a value expression evaluates to an email address no user holds: no authorable key, spelling or stored shape moves, the parse is unchanged, and no stored flow is read, rewritten or converted. The repair is the author's choice between an email-field recipient naming a field of the trigger record and giving the person a user, which no ledger entry can derive, and no in-repo flow mails a computed address (no `notify` node in `examples/**` at `47af3dd` names the `email` channel). The other categories are closed on facts: the package publishes (not unpublished); no ADR-0087 id covers this verdict (not already-registered); and nothing exported is removed or narrowed (not runtime-interface-only, type-surface-only or runtime-export-retired). --> |
0 commit comments