Repository navigation
docs(spec): a notify node's recipients names a written-out email address as an author constant, not an audience selector - #25739
objectstack-fleet[bot] wants to merge 10 commits into
Conversation
… constant
The `recipients` docblock no longer lists "an email address" among the
audience selectors. A string address the author writes out is named an
author constant, sent as written; an address a CEL envelope evaluates to
resolves to the user who holds it, and failing the node on one that no
user holds, pointing to `{ emailField }`, is stated as planned: the
runtime half has not landed. Text only: no key, shape or default moves.
Claude-Session: https://claude.ai/code/session_01PC4NPXPgPmpDRG9DdDnyXT
Co-authored-by: Claude <noreply@anthropic.com>
…he changeset Claude-Session: https://claude.ai/code/session_01PC4NPXPgPmpDRG9DdDnyXT Co-authored-by: Claude <noreply@anthropic.com>
…tify-recipients-describe
📓 Docs Drift Check2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 144 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 430c5b118021c4984dc1be5e2c3bf819f2e26839 && git checkout 430c5b118021c4984dc1be5e2c3bf819f2e26839
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 6679644a1a56b552d6864b125900221823ec78b3 0433b8133fd84f7ffe6cb2ea532dc52fc6faade0 && git checkout -B drift-repro 6679644a1a56b552d6864b125900221823ec78b3 && git merge --no-ff 0433b8133fd84f7ffe6cb2ea532dc52fc6faade0
node scripts/docs-audit/affected-docs.mjs --json 6679644a1a56b552d6864b125900221823ec78b3 |
|
CI red, not this PR's · Two checks are red on
Cause: #25734 ( Fix: card #25742, claimed by this seat, with the ledger repoint dispatched now (claim Generated by Claude Code |
…tify-recipients-describe
…mail-field delivery the runtime now makes The runtime half landed in d1e329e. A computed recipient that reads as an address, in a node whose channels include `email`, now resolves to the user who holds it or fails the node pointing to `{ emailField }`. An email-field recipient is mailed, read from the stored trigger row, one plain ASCII mailbox, at most 5 sends per address per hour, best-effort. The describes, the union description, the docblock and the changeset now say so, in place of the "planned" and "not live yet" sentences. Claude-Session: https://claude.ai/code/session_01PC4NPXPgPmpDRG9DdDnyXT Co-authored-by: Claude <noreply@anthropic.com>
…e made recipients text Claude-Session: https://claude.ai/code/session_01PC4NPXPgPmpDRG9DdDnyXT Co-authored-by: Claude <noreply@anthropic.com>
…ips in the same release Its last paragraph said delivery lands separately and that a run reaching such a node fails there. The runtime half landed in d1e329e and its own changeset is pending beside this one, so the paragraph now says delivery ships in this same release in service-automation's notify node. Claude-Session: https://claude.ai/code/session_01PC4NPXPgPmpDRG9DdDnyXT Co-authored-by: Claude <noreply@anthropic.com>
…erge-base text A correction to another lane's pending release note is refused by the release-note gate until a person confirms it, which would hold this PR's landing on a release decision that is not this PR's. The stale paragraph is routed to the maintainer as a separate decision instead. Claude-Session: https://claude.ai/code/session_01PC4NPXPgPmpDRG9DdDnyXT Co-authored-by: Claude <noreply@anthropic.com>
…irmed The maintainer confirmed that ruling B's screen of a computed address applies only to a notify node whose channels include email (ruling record 6393736930). The recipients docblock and the changeset no longer call the scope unconfirmed. Claude-Session: https://claude.ai/code/session_01PC4NPXPgPmpDRG9DdDnyXT Co-authored-by: Claude <noreply@anthropic.com>
席位核对ACCEPT · #25696 ·
|
Contract reviewServed-tier: Reviewer: Inputs: card #25696 (body and all 10 comments), ruling ① Derived judgmentsAccept set: unchanged — right. Every changed line in Public surface: no export moves — right. Every sentence of the new text, read against
Gate verdicts on this head — the check-runs. 43 check-runs ② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Contract reviewServed-tier: Reviewer: Inputs: card #25696 (no comment newer than the prior record), PR #25739 (body unchanged: ① Derived judgments1. The PR's own change is unchanged on this head — confirmed. 2. What 3. Every sentence of the new text, re-read against the runtime at
4. The prior record's one forward-looking line is superseded, not falsified. 5. Gate verdicts on this head — the check-runs. CI on the merge head started 2026-10-25T14:05Z. First read, 2026-10-25T14:12Z: 45 check-runs, 0 red, 11 still running. Completed ② Semver levelUnchanged from ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #25696
Clause-②: no
Metadata-protocol: semantic
Ruling-ref: 6386629237
What this changes
The
domain:spechalf of ruling B on #25647 (6386629237, batch #12 item 2, maintainer 「25635 25647 25675 同意」):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.recipientsdocblock (theNotifyRecipientsSlotSchemaJSDoc):team:ID)".emailchannel, to the address itself.d1e329e96d,screenComputedAddressesin service-automation's notify node) makes. In a node whosechannelsincludeemail, a computed recipient that reads as an address, bare or asuser: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 }.emailmails nobody, and its computed recipients reach the messaging resolver unscreened, as before. That channel scope is confirmed (ruling record 6393736930 on [Decision] A notify node already mails a written-out or computed email address: does ruling 6377842908's 「⛔ 不接受自由文本或表达式拼出的地址」 reach it — refuse both (A), refuse computed addresses only (B), or leave both (C)? #25647)..describe()(what the reference docs and the JSON Schema read) and the union's.meta()description say the same..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 refusedrecipientsvalue 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, "recipientsis who the notification goes to", is unchanged (pinned byio-node-config.test.ts).{ emailField }"the one recipient form that is an email ADDRESS". It is the form for an address the record supplies, and it is delivered sinced1e329e96d. An address written out by the author is the other way to one.content/docs/references/automation/io-node-config.mdxwithgen:docs, and added the changeset.changeset/25696-notify-recipients-describe.md(@objectstack/spec:minor, the levelMetadata-protocol: semantictakes).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 mergedpackages/services/service-automation/src/builtin/notify-node.ts:resolveRecipientsmarks each recipient that an envelope computed and that reads as an address (mailedAddressOf: bare, or the remainder afteruser:).screenComputedAddressesruns only whentoStringList(cfg.channels).includes('email').resolveEmailFieldAddressreads the stored row and requiresisOneAddress.admitEmailFieldSendcaps sends atEMAIL_FIELD_SENDS_PER_WINDOW(5) perEMAIL_FIELD_SEND_WINDOW_SECONDS(3600), and admits on a counter fault.What each claim in the new text rests on
emailchannel, the address itself:RecipientResolver.resolveOne→resolveEmail(asys_userlookup byemail, verbatim on a miss), thenemail-channel.ts'sEMAIL_SHAPE(recipient)branch.screenComputedAddressesnever sees a written-out value.screenComputedAddresses, quoted above.mailsgate inregisterNotifyNodepassesaudience.value.recipientsthrough unscreened.resolveEmailFieldAddress,isOneAddressandadmitEmailFieldSend.Verification (head
83ce3e8bad,origin/maind1e329e96dmerged in)83ce3e8baddiffers from2e4fad24c5, 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 --commandsfor the two changed paths derives 83 commands. 82 exit 0.check:livenessexits 1 on the main-causedtranslation/flows.screensrow.--ranprints:✓ dispatch-gates --ran: 83 derived famil(ies) accounted for — 83 run, 0 NOT-MEASURED.check:generated: its only red lane ischeck:liveness.Spec
typecheck: VERDICT 0.Spec
test:1 failed | 720 passed (721),23 failed | 21810 passed. All 23 failures are incheck-liveness.test.ts, the main-caused file.Spec
test:repo:79 passed (79),1269 passed (1269).The changeset family and
check:nul-bytesexit 0.pnpm install --frozen-lockfileexit 0. Fullturbo run build --filter=!@objectstack/docs --concurrency=2: VERDICT command-exit 0.check:generatedafter the text change namedcontent/docs/references/**stale. Aftergen:docsthe reference is regenerated, andcheck:docsexits 0.pnpm --filter @objectstack/spec test:Test Files 1 failed | 720 passed (721),Tests 23 failed | 21810 passed | 1 todo (21834).scripts/liveness/check-liveness.test.ts, which reads service-automation'sengine.ts.0189524a41, onmain) moved that file's cited code out, and the fix is main red: Spec property liveness — translation/flows.screens cites service-automation's engine.ts, whose only 'screens' mention moved to run-resume.ts in #25734 #25742.6cfe103516, before0189524a41was 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.Derived gates at
2e4fad24c5: 107 commands, 105 exit 0, and 2 exit 1 (check:liveness, andcheck:generatedbecause it runscheck:liveness).translation/flows.screens → packages/services/service-automation/src/engine.ts, the same main-caused row.--ranprints:✓ 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-bytesexits 0.No ablation: no tested behaviour changes here.
Acceptance notes
content/docs/automation/flows.mdxnow carries feat(service-automation)!: notify mails an email-field recipient, limited per address, and refuses a computed address no user holds #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.:340still lists a user id andteam:IDand never calls a bare address a selector, so it is not false. The runtime's own texts and the migration entry18.flow-value-slot-template-dialect-refused.ts:90are not false either..changeset/25610-notify-email-field-recipient.mdsays delivery "lands separately; until it does, a run that reaches such a node fails at the node". That has been false since feat(service-automation)!: notify mails an email-field recipient, limited per address, and refuses a computed address no user holds #25721 (d1e329e96d). This PR does not correct it: the release-note gate (finding: random changeset filenames collide silently across parallel agents — a round overwrote a sibling PR'sminorchangeset and every gate stayed green #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, [Decision] The pending release note .changeset/25610-notify-email-field-recipient.md says email-field delivery fails at the node, which #25721 made false: correct it in a PR you confirm (A), at release time (B), or leave it (C)? #25754.7981384,packages/app-shell/src/views/metadata-admin/inspectors/FlowRecipientsField.tsx:15: the "Email address" row stores an author constant, unaffected.:12) stores{ dialect: 'cel', source: 'record.FIELD' }. Over anemail-typed field on anemailnode, that is now refused unless a user holds the address. Routing such fields to{ emailField }belongs to the runtime lane.8a14b56: no written-out recipient address.examples/app-showcase/src/automation/flows/index.ts:exec@example.com(:1435) andadmin@objectos.ai×4 (:430,:1454,:1567,:1730) are author constants. No showcasenotifynode names theemailchannel.Test Core (1/6)andSpec property livenessare red onmain's0189524a41(check-liveness.test.ts/check:liveness), fixed by main red: Spec property liveness — translation/flows.screens cites service-automation's engine.ts, whose only 'screens' mention moved to run-resume.ts in #25734 #25742 (PR fix(spec/liveness): drop the comment-only engine.ts pointer from the translation/flows.screens row #25751), which is not landed yet. They go green on the next merge ofmainonce it lands.Generated by Claude Code