Repository navigation
Commit 52f3f51
docs(spec): a notify node's
Fixes #25696
Clause-②: no
Metadata-protocol: semantic
Ruling-ref: 6386629237
## What this changes
The `domain:spec` half of ruling B on #25647 (`6386629237`, batch #12
item 2, maintainer 「25635 25647 25675 同意」):
> 「**B** = 只关「表达式算出来的地址」:`notify` 收件人若由表达式(CEL
信封)求值得到一个不属于任何用户的邮箱地址,节点失败并给出定位提示指向 `{ emailField
}`;求值得到某个用户的地址仍解析为该用户。元数据里**写死**的地址是作者常量,照常发出。」
Its spec workload: 「`domain:spec` 改描述文字,把「写死地址」写成作者常量,并从受众选择器列表里去掉笼统的「an
email address」。」
Text only, all in `packages/spec/src/automation/io-node-config.zod.ts`.
No key, shape, default, enum or accepted value moves; everything that
parsed still parses.
- **The `recipients` docblock** (the `NotifyRecipientsSlotSchema`
JSDoc):
- "an email address" is gone from the audience-selector list, which now
reads "(such as `team:ID`)".
- A string address the author writes out is named an **author
constant**, sent as written: to the user who holds it, or else, on the
`email` channel, to the address itself.
- A paragraph states what the runtime half (#25721, `d1e329e96d`,
`screenComputedAddresses` in service-automation's notify node) makes. In
a node whose `channels` include `email`, a computed recipient that reads
as an address, bare or as `user:ADDRESS`, must be a user's. One a user
holds resolves to that user. One that no user holds, or one that cannot
be looked up, fails the node, pointing to `{ emailField }`.
- A node that does not name `email` mails nobody, and its computed
recipients reach the messaging resolver unscreened, as before. That
channel scope is confirmed (ruling record 6393736930 on #25647).
- A second paragraph states the email-field delivery the same change
makes: the address is read from the stored trigger row and must be one
plain ASCII mailbox. One address receives at most 5 sends per hour,
best-effort.
- **The field `.describe()`** (what the reference docs and the JSON
Schema read) and **the union's `.meta()` description** say the same.
- The `.describe()` sentence "Delivery to an email-field recipient is
not live yet: until the messaging service resolves the form, a run that
reaches the node fails there" is replaced with what the runtime does.
- **`NOTIFY_RECIPIENTS_RULE`**, the sentence a refused `recipients`
value leads with, now lists "an email address written out, an author
constant sent as written" beside a user id and an audience selector. A
refused author is told every string spelling. Its opening, "`recipients`
is who the notification goes to", is unchanged (pinned by
`io-node-config.test.ts`).
- **The email-field section note** no longer calls `{ emailField }` "the
one recipient form that is an email ADDRESS". It is the form for an
address the record supplies, and it is delivered since `d1e329e96d`. An
address written out by the author is the other way to one.
- Regenerated `content/docs/references/automation/io-node-config.mdx`
with `gen:docs`, and added the changeset
`.changeset/25696-notify-recipients-describe.md` (`@objectstack/spec`:
`minor`, the level `Metadata-protocol: semantic` takes).
## The refusal is made: the runtime half landed
When this PR opened, the computed-address refusal was worded as planned,
because the runtime half was still open (triage `6387591836`). PR #25721
(`d1e329e96d`) has since landed the runtime half for both #25571 and
#25647, and this branch merged it (`f6c07d3953`). The text now states
what is made, read from the merged
`packages/services/service-automation/src/builtin/notify-node.ts`:
- `resolveRecipients` marks each recipient that an envelope computed and
that reads as an address (`mailedAddressOf`: bare, or the remainder
after `user:`).
- `screenComputedAddresses` runs only when
`toStringList(cfg.channels).includes('email')`.
- `resolveEmailFieldAddress` reads the stored row and requires
`isOneAddress`.
- `admitEmailFieldSend` caps sends at `EMAIL_FIELD_SENDS_PER_WINDOW` (5)
per `EMAIL_FIELD_SEND_WINDOW_SECONDS` (3600), and admits on a counter
fault.
- A written-out address never reaches the screen.
## What each claim in the new text rests on
- *A written-out address reaches the user who holds it, or else, on the
`email` channel, the address itself:* `RecipientResolver.resolveOne` →
`resolveEmail` (a `sys_user` lookup by `email`, verbatim on a miss),
then `email-channel.ts`'s `EMAIL_SHAPE(recipient)` branch.
`screenComputedAddresses` never sees a written-out value.
- *Computed address, email node:* `screenComputedAddresses`, quoted
above.
- *Computed address, non-email node:* the `mails` gate in
`registerNotifyNode` passes `audience.value.recipients` through
unscreened.
- *Email-field delivery:* `resolveEmailFieldAddress`, `isOneAddress` and
`admitEmailFieldSend`.
## Verification (head `83ce3e8bad`, `origin/main` `d1e329e96d` merged
in)
`83ce3e8bad` differs from `2e4fad24c5`, where the build and the
consumer-suite readings below were taken, only in a docblock comment and
the changeset. A round-2 edit to
`.changeset/25610-notify-email-field-recipient.md` (`aed84ec582`) was
restored byte-identical to the merge base (`29733bf86f`). Round 3
(`83ce3e8bad`) states the confirmed email-channel scope.
Readings at `83ce3e8bad`:
- Spec rebuilt: VERDICT 0.
- `dispatch-gates --commands` for the two changed paths derives 83
commands. 82 exit 0. `check:liveness` exits 1 on the main-caused
`translation/flows.screens` row.
- `--ran` prints: `✓ dispatch-gates --ran: 83 derived famil(ies)
accounted for — 83 run, 0 NOT-MEASURED`.
- `check:generated`: its only red lane is `check:liveness`.
- Spec `typecheck`: VERDICT 0.
- Spec `test`: `1 failed | 720 passed (721)`, `23 failed | 21810
passed`. All 23 failures are in `check-liveness.test.ts`, the
main-caused file.
- Spec `test:repo`: `79 passed (79)`, `1269 passed (1269)`.
- The changeset family and `check:nul-bytes` exit 0.
- `pnpm install --frozen-lockfile` exit 0. Full `turbo run build
--filter=!@objectstack/docs --concurrency=2`: VERDICT command-exit 0.
- `check:generated` after the text change named
`content/docs/references/**` stale. After `gen:docs` the reference is
regenerated, and `check:docs` exits 0.
- `pnpm --filter @objectstack/spec test`: `Test Files 1 failed | 720
passed (721)`, `Tests 23 failed | 21810 passed | 1 todo (21834)`.
- All 23 failures are in `scripts/liveness/check-liveness.test.ts`,
which reads service-automation's `engine.ts`.
- #25734 (`0189524a41`, on `main`) moved that file's cited code out, and
the fix is #25742.
- The same suite was 721/721 green at `6cfe103516`, before `0189524a41`
was merged in.
- `pnpm --filter @objectstack/spec test:repo`: `79 passed (79)` files,
`1269 passed (1269)` tests, VERDICT 0.
- `pnpm --filter @objectstack/spec typecheck`: VERDICT 0.
- Full suites of the packages that read the contract:
- `@objectstack/lint`: `161 passed (161)` files, `7285 passed (7285)`
tests.
- `@objectstack/service-automation`: `202 passed (202)` files, `2659
passed (2659)` tests.
- `@objectstack/metadata-protocol`: `263 passed | 3 skipped (266)`
files, `21485 passed | 19 skipped (21504)` tests.
- Each VERDICT 0.
- Derived gates at `2e4fad24c5`: 107 commands, 105 exit 0, and 2 exit 1
(`check:liveness`, and `check:generated` because it runs
`check:liveness`).
- Both name only `translation/flows.screens →
packages/services/service-automation/src/engine.ts`, the same
main-caused row.
- `--ran` prints: `✓ dispatch-gates --ran: 107 derived famil(ies)
accounted for — 107 run, 0 NOT-MEASURED (a DERIVED zero — all 107
recorded an exit code and none of them is 3).`
- `affected-docs`: `0 docs name something this change touched`.
- Control-byte self-scan: no hits. `check:nul-bytes` exits 0.
- No ablation: no tested behaviour changes here.
## Acceptance notes
- **Hand-written docs:** `content/docs/automation/flows.mdx` now carries
#25721's paragraphs on the email-field form and the computed-address
rule (from `:359`). They match the runtime and this text, so I changed
nothing there. `:340` still lists a user id and `team:ID` and never
calls a bare address a selector, so it is not false. The runtime's own
texts and the migration entry
`18.flow-value-slot-template-dialect-refused.ts:90` are not false
either.
- **Pending changeset made false by #25721, not by this PR:** the last
paragraph of `.changeset/25610-notify-email-field-recipient.md` says
delivery "lands separately; until it does, a run that reaches such a
node fails at the node". That has been false since #25721
(`d1e329e96d`). This PR does not correct it: the release-note gate
(#17712, `check-empty-changeset`) holds a correction of another PR's
pending changeset red until a person confirms it, so the seat has filed
it for the maintainer as a separate decision, #25754.
- **Downstream producers, measured (anonymous reads):**
- objectui `7981384`,
`packages/app-shell/src/views/metadata-admin/inspectors/FlowRecipientsField.tsx:15`:
the "Email address" row stores an author constant, unaffected.
- The "Record field" row (`:12`) stores `{ dialect: 'cel', source:
'record.FIELD' }`. Over an `email`-typed field on an `email` node, that
is now refused unless a user holds the address. Routing such fields to
`{ emailField }` belongs to the runtime lane.
- hotcrm `8a14b56`: no written-out recipient address.
- `examples/app-showcase/src/automation/flows/index.ts`:
`exec@example.com` (`:1435`) and `admin@objectos.ai` ×4 (`:430`,
`:1454`, `:1567`, `:1730`) are author constants. No showcase `notify`
node names the `email` channel.
- **CI:** `Test Core (1/6)` and `Spec property liveness` are red on
`main`'s `0189524a41` (`check-liveness.test.ts` / `check:liveness`),
fixed by #25742 (PR #25751), which is not landed yet. They go green on
the next merge of `main` once it lands.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01PC4NPXPgPmpDRG9DdDnyXT)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: objectstack-fleet[bot] <332303061+objectstack-fleet[bot]@users.noreply.github.com>recipients names a written-out email address as an author constant, not an audience selector (#25739)1 parent 4034f47 commit 52f3f51
3 files changed
Lines changed: 74 additions & 18 deletions
File tree
- .changeset
- content/docs/references/automation
- packages/spec/src/automation
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
98 | | - | |
| 98 | + | |
99 | 99 | | |
100 | 100 | | |
101 | 101 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
248 | 248 | | |
249 | 249 | | |
250 | 250 | | |
251 | | - | |
252 | | - | |
253 | | - | |
254 | | - | |
255 | | - | |
256 | | - | |
257 | | - | |
258 | | - | |
259 | | - | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
260 | 263 | | |
261 | 264 | | |
262 | 265 | | |
263 | 266 | | |
264 | 267 | | |
265 | 268 | | |
266 | | - | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
267 | 272 | | |
268 | 273 | | |
269 | 274 | | |
| |||
321 | 326 | | |
322 | 327 | | |
323 | 328 | | |
324 | | - | |
325 | | - | |
326 | | - | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
327 | 333 | | |
328 | 334 | | |
329 | 335 | | |
| |||
352 | 358 | | |
353 | 359 | | |
354 | 360 | | |
355 | | - | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
356 | 366 | | |
357 | 367 | | |
358 | 368 | | |
| |||
363 | 373 | | |
364 | 374 | | |
365 | 375 | | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
366 | 397 | | |
367 | 398 | | |
368 | 399 | | |
| |||
392 | 423 | | |
393 | 424 | | |
394 | 425 | | |
395 | | - | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
396 | 429 | | |
397 | 430 | | |
398 | 431 | | |
| |||
635 | 668 | | |
636 | 669 | | |
637 | 670 | | |
638 | | - | |
639 | | - | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
640 | 679 | | |
641 | 680 | | |
642 | 681 | | |
| |||
0 commit comments