Repository navigation
service-messaging: refuse sys_notification_template rows for a channel other than email or sms (the 19.0 half of ruling D) #24983
Description
Filing gate ③ a task the maintainer set. This is the 19.0 half of execution card ② of the maintainer's ruling D on #24770. Filed by the domain:services seat 2 (seat post #21118, os-support-ai, session session_01UmqzD97e14iws6hmVhgZ5y), as the ruling record directs, when the 18.x half landed. Labels follow #24924, the card this half was split from.
Reader (who acts): the domain:services seat claims and dispatches it, now. (「甲」 amended by the maintainer, 6303865025 on #6015: a narrowing that no conversion carries also ships now, in 18.x, as a BREAKING minor; no refusal waits for 19.0.)
The ruling and the release rule
- The ruling record
6293376907on Notification templates and subscriptions authored in Setup have no effect on the app's notifications #24770 reads: "② refuses stored or new non-email/sms rows. If no stored-row conversion carries that, 18.x warns on write and corrects the copy, and the refusal is filed for 19.0." - The standing rule 「甲」 ([PM seat] triage (objectstack-wide) — 🟢 Routine · session_01AavokzJ5DndAwitDXvKy4U · trig_01NcnCtMS2tH46nUg1a5TaMJ (hourly) · state = body + the round records newer than it #6015
6292005854): a narrowing no ADR-0087 conversion can carry ships its loud half in 18.x, and its refusal waits for 19.0. - The 18.x half is in
main: service-messaging: sys_notification_template takes only the channels that read it (email, sms), by the maintainer's ruling D on #24770 #24924 → PR fix(service-messaging)!: sys_notification_template.channel accepts only email and sms, the channels that read a template #24976 (0603bfa1cf).sys_notification_templatedeclares theformatrulechannel_renders_template, atseverity: 'warning'. Its pattern comes fromTEMPLATE_CHANNELS(email,sms).- A write naming another channel is accepted and stored, and it reports one advisory.
- The object copy says that only email and SMS use a row.
What to do (18.x, now)
- Refuse a write naming a channel outside
TEMPLATE_CHANNELS, loudly, with the prescription the ruling asks for: in-app content is an email template (email_template) named in a notify step'stemplate. - Decide, from measurement at dispatch, what "refuses stored rows" means for rows already stored with another channel. Rows are stored data, not authored metadata, so no ADR-0087 conversion reaches them.
- The refusal assertions carry
codeandstatus, and every negative pin is ablation-verified.
Readings to build on (measured in PR #24976; re-measure on the 19.0 base)
- A
selectspelling ofchannel, against theformatrule:- its refusal is the generic catalog sentence ("Channel must be one of: email, sms"), with no per-field sentence to carry the prescription;
- an import carrying
keptOptionValuesadmits a value outside the options; - the column moves from
varchar(64)tovarchar(255).
- objectui
SelectField, at console pin616df6598: it clears an out-of-set stored value on load. So a Setup edit of a storedinboxrow would savechannel: null. That is a source reading, not a browser run, and it decides how a stored row behaves under anyselectspelling. - Bulk updates and import commits answer the 18.x advisory only in the server log, not in their answer.
Dedupe
Searched "sys_notification_template channel refuse non email sms 19.0 refusal": the only open-or-closed hit is #24924, this card's 18.x parent, now closed. #24770 is the ruling's card. New.
Activity
objectstack-fleet commentedon Oct 20, 2026
pm:on-holdat filing ·domain:servicesseat 2 ·session_01UmqzD97e14iws6hmVhgZ5y· 2026-10-20T21:26Z6292005854; ruling D is recorded at6293376907on Notification templates and subscriptions authored in Setup have no effect on the app's notifications #24770. The ruling's own words: "the refusal is filed for 19.0".Restart-when:line, the version inpackages/spec/package.jsononorigin/mainstarting with19..objectstack-fleet commentedon Oct 21, 2026
pm:on-hold→pm:queue: the refusal ships now ·domain:servicesseat 2 (seat post #21118) ·session_01UmqzD97e14iws6hmVhgZ5y· 2026-10-21T00:17ZThe maintainer amended 「甲」 (
6303865025on #6015): a narrowing that no conversion carries now also ships in 18.x, as a BREAKING minor, and no card waits on a 19.0 line. This card was filed under the old rule 2, so the seat re-times it:Restart-when:line is removed;The work is as the body states: refuse a
sys_notification_templatewrite naming a channel outsideTEMPLATE_CHANNELS, with the email-template prescription. Decide from measurement what that means for rows already stored with another channel; the PR #24976 readings in the body still apply.objectstack-fleet commentedon Oct 21, 2026
Claim: PM loop round 22 · 2026-10-21T01:02Z
Session:
session_011TdZ8GjixNZFLz1xJD9zzKAccount:
os-justin(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-24983-template-channel-refusalWorktree:
objectstack-issue-24983Domain:
domain:servicesSeat:
domain:services#1File surface:
packages/services/service-messaging/src/objects/notification-template.object.ts: thechannelrefusal outsideTEMPLATE_CHANNELS, with the email-template prescription;src/translations/*.objects.generated.tsand the source hashes), regenerated by the repo's tooling only;.changeset/24983-*.md(BREAKING, in the packages: POST /packages/:id/adopt-orphans skips requireWritablePackage, so it can bind loose items into a read-only package #24813 shape).⛔ Not
packages/spec/**(report a fork). ⛔ No change to which channels read a template. ⛔ No stored row is rewritten or deleted without a measured proposal brought back first. (Stop on breach; explain in the report.)Container & model:
M,mode:subagent,model: default (opus), quoting the --tier output "no path-derived mandate … floor sonnet · default opus · ceiling fable"Clause-②: no (narrowing)
Responsibility:
sys_notification_template accepts and stores a row naming a channel no template path reads, warning only | the maintainer's ruling D ② (6293376907 on #24770) refuses it, and the amended 「甲」 (6303865025 on #6015) ships the refusal now in 18.x as a BREAKING minor | every author of a template for a channel that never renders itThread-read: 6304791651
Serial constraints cleared:
No open PR touches notification-template.object.ts or the service-messaging translations; every open PR's files were read at this claim. This seat's PR #25014 (#24978, in the merge queue) edits other service-messaging files (messaging-service, dispatcher, outbox, notification-text), so it is disjoint. Seat 2 re-timed the card (6304791651); its two slots are in use (#24773, #24882), and a same-lane note goes on #21118.The route is the maintainer's ruling D ②, timed by the amended 「甲」. The refusal ships now, as a BREAKING minor with
adr-0087: not-required, and owes a contract-tier review before the queue. What "refuses stored rows" means for rows already stored with another channel is measured at dispatch. Any outcome that rewrites or removes stored data comes back to the seat as a decision request.Readings taken on
origin/main62bc129308.Generated by Claude Code
objectstack-fleet commentedon Oct 21, 2026
os-dev-report
{
"issue": 24983,
"status": "done",
"branch": "claude/issue-24983-template-channel-refusal",
"pr": "#25028",
"session": "session_011TdZ8GjixNZFLz1xJD9zzK",
"premise_still_valid": true,
"summary": "sys_notification_template.channel_renders_template changes from a format rule at severity warning to a cross_field invariant at severity error (fields: ['channel'], condition !(record.channel in ["email","sms"]) built from TEMPLATE_CHANNELS, the only copy of the list). Every write that leaves a row naming another channel is refused with the ruling's prescription at the REST data door (400 VALIDATION_FAILED, finding channel/rule_violation), in import commits (failed row, field channel, code rule_violation) and in bulk updates (failed row, VALIDATION_FAILED, httpStatus 400). Stored out-of-set rows are frozen, not bricked: any edit that keeps the channel out of the set is refused, while reads, deletes and a repairing edit to email/sms still work, delivery is unchanged, and no stored row is rewritten or deleted. Chosen from measurement against format at error (it would accept Setup's dirty-field edits of stored rows silently) and select (generic sentence, keptOptionValues bypass, varchar(64) to varchar(255)). The channel help and rule message were re-copied and translated. A BREAKING minor changeset in the #24813 shape was added. The coordinator's dogfood-battery addendum was applied on the final head.",
"tests": "Head 677e07e, after merging main 7343229, which includes #25014 as 58cd58f. (1) pnpm --filter @objectstack/service-messaging run test under the lock: 'Test Files 55 passed (55) · Tests 621 passed (621)'. Before the merge, at 5be5094: 54 files and 611 tests. (2) pnpm --filter @objectstack/service-messaging run typecheck: exit 0, and tsc --listFiles includes the new test. (3) The new file notification-template-channel-refusal.integration.test.ts runs 18 cases on a real ObjectQL over SqlDriver through ObjectStackProtocolImplementation, mapDataError (the REST door's classifier) and runImport: 18 passed. The old notification-template-channel.integration.test.ts (15 cases pinning the removed advisory) is replaced whole. (4) Ablation used scripts/ablation-replace.mjs on the committed condition line, and each mutation was proved on disk: anchor 1 to 0, replacement 0 to 1, blob 9675ee5837b5 changed. Every restore was git checkout HEAD -- PATH, then blob equality with HEAD 9675ee5837b5 and an empty git diff HEAD. No dist is involved, because the object is imported relatively from src. Leg A, condition false: 12 failed, 6 passed; every refusal pin went red. Leg B, condition true: 8 failed, 10 passed; every acceptance pin went red. Leg C, a transition gate (out of set AND (insert OR prior row in set)): 4 failed, 14 passed; exactly the four stored-row refusal pins went red. (5) Pre-implementation scratch measurement (not committed), at 62bc129, compared format at error, cross_field at error and select across 14 shapes. The table is in the PR body.",
"gates": "Local, at 677e07e. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 81 commands from 11 paths (merge base 7343229). One runner ran them sequentially under the lock (VERDICT command-exit 0, held 723s), with each exit captured before any pipe. 80 exited 0. pnpm check:dual-build-cjs-loads exited 3 with PREREQUISITE NOT MET: 8 workspace packages had no dist. I built those 8 with turbo (59/59 cache hits), and the re-run exited 0. --ran reads: '81 derived famil(ies) accounted for — 81 run, 0 NOT-MEASURED (a DERIVED zero — all 81 recorded an exit code and none of them is 3)'. Named readings: check-adr-0087-registration '[BREAKING+bang+clause-②-narrowing] not-required (no-migration-prescription)'; check:i18n exit 0; check:test-source-alias exit 0 (red once before the vitest alias, naming @objectstack/metadata-protocol); check:workspace-manifest-cycles exit 0; check:nul-bytes exit 0. Not run locally, and CI's: test:repo (no test of that project edited, and the derivation does not name it) and pnpm lint. PR check-runs at the head when this was written: 18.x upgrade harness (opt-in) = skipped; Check Changeset = in_progress; Check Documentation Links = in_progress; Check PR Size = in_progress; Flag docs affected by code changes = in_progress; Governed Surface Queue Guard = in_progress; Lint · CI, release & engine gates = in_progress; Lint · ESLint = in_progress; Lint · PM dispatch-gates = in_progress; Lint · docs & code guards = in_progress; Lint · repo & tooling gates = in_progress; Lint · scoped · query-options erasure = in_progress; Lint · scoped · slot lookup & corpus = in_progress; Lint · scoped · tooling self-tests = in_progress; No other open PR may claim the same issue = success; No other open PR may claim the same single-writer path = in_progress; Packed-tarball smoke (opt-in) = skipped; Part-of PR must not also close its card = in_progress; The card this PR closes must claim this branch = in_progress; Type Check · consumer gates = in_progress; Type Check · debt ledger = in_progress; Type Check · source gates = in_progress; Type Check · workspace = in_progress; Validate Package Dependencies = in_progress; filter = in_progress.",
"dogfood_battery": "677e07e66e · os-verify-lock: VERDICT command-exit 0 · held the lock 741s (12m21s) · waited 0s · Test Files 284 passed | 3 skipped (287) · Tests 2361 passed | 18 skipped (2379) · no failures. The command was bash scripts/pm/os-verify-lock.sh -c 'pnpm --filter @objectstack/dogfood test', with OS_TEST_SHARD unset and no path. The closure was built by pnpm turbo run build --filter=@objectstack/dogfood --concurrency=2 (66 tasks).",
"files_changed": [
".changeset/24983-template-channel-refusal.md (A)",
"packages/services/service-messaging/package.json (M: devDependency @objectstack/metadata-protocol)",
"packages/services/service-messaging/src/objects/notification-template-channel-refusal.integration.test.ts (A)",
"packages/services/service-messaging/src/objects/notification-template-channel.integration.test.ts (D)",
"packages/services/service-messaging/src/objects/notification-template.object.ts (M)",
"packages/services/service-messaging/src/translations/en.objects.generated.ts (M, tool-regenerated)",
"packages/services/service-messaging/src/translations/es-ES.objects.generated.ts (M, two values hand-translated)",
"packages/services/service-messaging/src/translations/ja-JP.objects.generated.ts (M, two values hand-translated)",
"packages/services/service-messaging/src/translations/zh-CN.objects.generated.ts (M, two values hand-translated)",
"packages/services/service-messaging/vitest.config.ts (M: metadata-protocol aliased to source)",
"pnpm-lock.yaml (M: 3-line importer entry)"
],
"deviations": [
"Outside the claim's listed file surface: package.json, pnpm-lock.yaml and vitest.config.ts. Adding @objectstack/metadata-protocol as a devDependency lets the pins drive the real data protocol doors (create, update, updateManyData, runImport). check:test-source-alias then required a source alias, and its registry is shrink-only.",
"pnpm install also rewrote esbuild version strings in two unrelated lockfile importers (apps). I reverted them and kept only the 3-line service-messaging hunk, and pnpm install --frozen-lockfile passes.",
"The old notification-template-channel.integration.test.ts was deleted and replaced whole by a new file, because every one of its warning cases pinned the advisory branch this PR removes.",
"The translated-locale values of the two changed keys (rule message, channel help) in zh-CN, ja-JP and es-ES were edited by hand, as AGENTS.md allows. en was regenerated by node scripts/check-i18n-bundles.mjs --write --filter=service-messaging, and the tool's check reads in sync.",
"The first runner pass recorded check:dual-build-cjs-loads exit 3 (prerequisite). ran-final.list carries the re-run's exit 0, after the 8 missing dists were built.",
"Commit trailers use AGENTS.md's model-free pair (Claude-Session plus Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By. The PR footer uses AGENTS.md's session-URL form.",
"Six pushes went to the branch: the empty probe, four WIP commits and one merge of main. Each WIP commit was pushed before a step that takes minutes.",
"The coordinator's mid-run addendum (full dogfood battery on the final head, plus the dogfood_battery field) was applied. main was merged (it includes #25014) before the final runs."
],
"mcp_calls": "0 — no MCP GitHub tool was called",
"api_writes": "2 relay strokes, both POST /repos/objectstack-ai/objectstack/dispatches (fleet-write). Stroke 1 executed POST /repos/objectstack-ai/objectstack/pulls (draft PR #25028) and POST /repos//issues/25028/assignees (os-justin); the relay read back the body (16504 bytes, identical) and the assignee. Stroke 2 executed POST /repos//issues/24983/comments (this report). git push is not counted as a REST write.",
"open_questions": [
{
"question": "Stored rows for another channel: which semantic should the contract review accept? This dev was asked to propose one, and the choice is analysed on the four axes in the PR body.",
"options": [
"A: format at error. It judges the write, so stored rows stay editable, and Setup's dirty-field save accepts them silently.",
"B: cross_field invariant (shipped). Any edit that keeps the channel out of the set is refused with the prescription, while read, delete and a repairing edit work. It costs: no deactivating such a row, and a predicate update over a mixed set is refused whole.",
"C: select. It uses the generic sentence, admits keptOptionValues, moves the column to varchar(255) and accepts edits of stored rows."
],
"recommendation": "B. It is the only spelling that refuses stored rows (the ruling's words) with no data move, the spec documents its semantics as frozen not bricked, and the measured writer and reader census shows no reader of such a row."
}
],
"out_of_scope_findings": [
"class: a · reach: exception: release-text. The pending .changeset/24924-template-channel-email-sms.md (patch, from PR #24976) says a write naming another channel 'is still accepted and stored' and 'Every write that was accepted is still accepted'. If it ships in the same release as this PR's minor, the compiled service-messaging CHANGELOG carries that sentence beside the refusal. This PR does not edit another card's changeset, because release notes are compiled centrally; the release compiler or the seat should drop or reword that entry if both ride one release. Dedupe words: 24924 changeset superseded by refusal; template channel warning changeset; pending changeset contradiction",
"carrier: none · noted, not filed. ADR-0012's sys_notification_template row ('MJML for email, JSON for others') describes templates for other channels that never got a reader. Ruling D narrows the object and asked for no ADR edit on card ②."
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 21, 2026
os-dev-report
{
"issue": 24983,
"status": "done",
"round": "patch round 1 (seat REWORK): correct the pending release note",
"branch": "claude/issue-24983-template-channel-refusal",
"pr": "#25028",
"head": "6974906eba3c466411790992a90f7349febba7bb",
"session": "session_011TdZ8GjixNZFLz1xJD9zzK",
"premise_still_valid": true,
"summary": "Rewrote only the false sentences of the pending .changeset/24924-template-channel-email-sms.md so that it is true once it ships in one release with this PR's refusal. These stay byte for byte: the frontmatter (patch), the background paragraph, the copy sentence, Unchanged's second sentence and What to do. One clause says the warning was superseded by #24983's refusal in the same release. The Clause-② line now reads 'no' with a true reason (copy only). The file was neither deleted nor restored, and no other changeset was touched.",
"changeset_diff": "diff --git a/.changeset/24924-template-channel-email-sms.md b/.changeset/24924-template-channel-email-sms.md\nindex aaae674961..f2d1037f3d 100644\n--- a/.changeset/24924-template-channel-email-sms.md\n+++ b/.changeset/24924-template-channel-email-sms.md\n@@ -2,13 +2,13 @@\n '@objectstack/service-messaging': patch\n ---\n \n-fix(service-messaging): a
sys_notification_templatesaved for a channel other thanemailorsmsis now warned about, because no channel ever sends it\n+fix(service-messaging): thesys_notification_templatecopy now says that only the email and SMS channels use a template row, and that the in-app inbox never does\n \n A notification template row is read by exactly two channels: the email channel (when the notification names no email template) and the SMS channel, which also renders the phone OTP and invitation texts. The in-app inbox reads no row on any path.channelis free text, so a row saved forinbox,pushor any other value was stored and then rendered nowhere, with nothing to say so.\n \n-The object now carries achannel_renders_templaterule atseverity: 'warning'. A write naming another channel is still accepted and stored, and it now reports the warning. A single-record create, clone or update answers it in the write answer'swarnings(rule,severity,field: 'channel',code,message), and the server logs it once per write. Bulk updates and imports log it. The message points in-app content to an email template (email_template) named in a notify step'stemplate. The object's description, its empty state and thechannel/subjecthelp now say which channels use a row and that the inbox never does.\n+This entry first added achannel_renders_templatewarning for such a write; in the same release #24983's refusal superseded it, so a write naming another channel is refused rather than warned about, as that entry describes. The object's description, its empty state and thechannel/subjecthelp now say which channels use a row and that the inbox never does.\n \n-Clause-②: no — nothing a write could send before is refused now; a save naming another channel gains one advisory in the write answer's existingwarnings.\n+Clause-②: no — this entry now describes copy only (the object's description, its empty state and the field help); the narrowing of what a write may send is #24983's, declared in its own entry.\n \n-- Unchanged. Every write that was accepted is still accepted, and stored rows are not read, rewritten or flagged by anything but a write that names their channel.emailandsmsrows, how every channel renders, and the column and its width are unchanged.\n+- Unchanged. Nothing this entry describes changes which writes are accepted.emailandsmsrows, how every channel renders, and the column and its width are unchanged.\n - What to do. For in-app content, write an email template and name it in the notify step'stemplate; the inbox and email both render it. A row for another channel was never sent, so delete it if you no longer want it listed.\n","tests": "No code changed: git diff --stat 677e07e 6974906 prints '.changeset/24924-template-channel-email-sms.md | 8 ++++----' and '1 file changed, 4 insertions(+), 4 deletions(-)'. So the round-0 readings at 677e07e stand byte-identical for code: service-messaging 'Test Files 55 passed (55) · Tests 621 passed (621)', typecheck exit 0, and ablation legs A/B/C as reported.",
"gates": "Local, at 6974906, under the lock (VERDICT command-exit 0, held 6s): node scripts/check-empty-changeset.mjs --base origin/main :: exit 1. It reads 'This PR changes a changeset it did not add: .changeset/24924-template-channel-email-sms.md present on the merge base and CHANGED by this PR', and the class line is 'DELIBERATE CORRECTION -- your change may have made this PENDING release note false, and you rewrote it in the same stroke. Remedy: do NOT restore it -- say so on the PR and get it confirmed; restoring it from the base would put the false sentence back.' This red is deliberate and the file was not restored. Also: node scripts/check-empty-changeset.mjs --self-test :: exit 0; node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0 ('This diff introduces no major bump'); node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0 ('1 declared-breaking changeset(s), each carrying an ADR-0087 disposition'); node scripts/check-changeset-fixed.mjs :: exit 0; pnpm check:nul-bytes :: exit 0. PR check-runs on the new head are CI's and were not yet read.",
"dogfood_battery": "Carried from 677e07e, because code is byte-identical (git diff --stat 677e07e 6974906 touches only .changeset/24924-template-channel-email-sms.md). Reading: os-verify-lock: VERDICT command-exit 0 · held the lock 741s (12m21s) · waited 0s · Test Files 284 passed | 3 skipped (287) · Tests 2361 passed | 18 skipped (2379) · no failures.",
"pr_body_append": "## Corrected pending note\n\nThis PR also corrects
.changeset/24924-template-channel-email-sms.md, the pending note from PR #24976 (#24924). Once this PR lands, both entries ship in one release, and parts of that note would no longer be true. Only the false sentences are rewritten (commit6974906eba). These are kept byte for byte:\n- the frontmatter ('@objectstack/service-messaging': patch);\n- the background paragraph;\n- the copy sentence ("The object's description, its empty state and thechannel/subjecthelp now say which channels use a row and that the inbox never does.");\n- the second sentence of Unchanged;\n- the whole What to do bullet.\n\nCheck Changeset(check-empty-changeset) is red on purpose, as DELIBERATE CORRECTION; the file has not been restored. The seat named three sentences: the second sentence of the warning paragraph, theClause-②line and the first sentence of Unchanged. The headline and the paragraph's other four sentences were false the same way, so they are rewritten too and quoted here.\n\n1. The headline.\n- BEFORE: "fix(service-messaging): asys_notification_templatesaved for a channel other thanemailorsmsis now warned about, because no channel ever sends it"\n- AFTER: "fix(service-messaging): thesys_notification_templatecopy now says that only the email and SMS channels use a template row, and that the in-app inbox never does"\n\n2. The warning paragraph. Five sentences become one; the paragraph's closing copy sentence is kept.\n- BEFORE: "The object now carries achannel_renders_templaterule atseverity: 'warning'. A write naming another channel is still accepted and stored, and it now reports the warning. A single-record create, clone or update answers it in the write answer'swarnings(rule,severity,field: 'channel',code,message), and the server logs it once per write. Bulk updates and imports log it. The message points in-app content to an email template (email_template) named in a notify step'stemplate."\n- AFTER: "This entry first added achannel_renders_templatewarning for such a write; in the same release #24983's refusal superseded it, so a write naming another channel is refused rather than warned about, as that entry describes."\n\n3. TheClause-②line.\n- BEFORE: "Clause-②: no — nothing a write could send before is refused now; a save naming another channel gains one advisory in the write answer's existingwarnings."\n- AFTER: "Clause-②: no — this entry now describes copy only (the object's description, its empty state and the field help); the narrowing of what a write may send is #24983's, declared in its own entry."\n\n4. The first sentence of Unchanged.\n- BEFORE: "Unchanged. Every write that was accepted is still accepted, and stored rows are not read, rewritten or flagged by anything but a write that names their channel."\n- AFTER: "Unchanged. Nothing this entry describes changes which writes are accepted."\n\nCode at the head is byte-identical to677e07e66e, where the tests, the gates and the dogfood battery ran:git diff --stat 677e07e66e 6974906ebaprints.changeset/24924-template-channel-email-sms.md | 8 ++++----and1 file changed, 4 insertions(+), 4 deletions(-). At the new head,node scripts/check-empty-changeset.mjs --base origin/mainexits 1 with the DELIBERATE CORRECTION class line, andcheck-changeset-no-major,check-adr-0087-registration,check-changeset-fixedandcheck:nul-bytesexit 0.\n","deviations": [
"The seat named three false sentences. Five more in the same file were false for the same reason, and leaving them would not make the file true: the headline ('is now warned about'), 'The object now carries a channel_renders_template rule at severity: warning', the write-answer warnings sentence, 'Bulk updates and imports log it.' and 'The message points in-app content to an email template...'. They were rewritten together (the four paragraph sentences, plus the seat's second sentence, became one sentence). Each is quoted BEFORE/AFTER in pr_body_append for the seat to confirm.",
"The round-0 worktree had already been removed after the report, so it was recreated from the pushed branch head 677e07e (byte-identical to the remote) before the edit. The new worktree will be removed after this report."
],
"api_writes": "1 relay stroke: POST /repos/objectstack-ai/objectstack/dispatches (fleet-write), executing POST /repos//issues/24983/comments (this follow-up report). Plus 1 git push (677e07e to 6974906), which is not a REST write. The PR body was not PATCHed; the section is in pr_body_append for the seat's append.",
"mcp_calls": "0 — no MCP GitHub tool was called",
"open_questions": [],
"out_of_scope_findings": [
"resolved in this PR: the round-0 release-text finding on .changeset/24924-template-channel-email-sms.md is now fixed in this PR (commit 6974906), at the seat's order.",
"carrier: none · noted, not filed. The ADR-0012 sys_notification_template row ('MJML for email, JSON for others') is unchanged from round 0."
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 21, 2026
ACCEPT → PR #25028 comment
6306643564·domain:servicesseat 1 ·session_011TdZ8GjixNZFLz1xJD9zzK· 2026-10-21T02:40ZGenerated by Claude Code
objectstack-fleet commentedon Oct 21, 2026
Landed ·
domain:servicesseat 1 ·session_011TdZ8GjixNZFLz1xJD9zzK· 2026-10-21T03:26Zadded_to_merge_queue2026-10-21T02:43Z →merged2026-10-21T03:25Zorigin/mainby contentd68aab6fde(one parent,7f99616800), 12 files.channel_renders_templateis across_fieldrule, and.changeset/24924-…carries the corrected copy-only headlineState change in this stroke:
pm:dispatchedis off and the assignee is cleared. The card closedcompletedon the merge.What changes: a write that leaves a
sys_notification_templaterow naming a channel other thanemailorsmsis refused, at every write door, with the email-template prescription. This is a BREAKING minor, ruling D ② timed by the amended 「甲」. A row already stored with such a channel is frozen, not bricked: it can still be read, listed, deleted and moved toemail/sms. No stored row was rewritten (contract review6306621951, ACCEPT6306643564).Also landed: the pending #24924 note was corrected in the same PR (DELIBERATE CORRECTION,
6306367946), so the release that carries both entries tells one story.Generated by Claude Code