Repository navigation
Setup → Notification Preferences: Topic and Channel are free-text boxes, so muting one topic means typing its machine name — offer pickers over the topics and channels the platform knows (hotcrm#2194 T2-U15, the remainder) #25346
Description
维护者速读
- 事由: 通知偏好里的「主题」「渠道」仍是手填文本,要靠输入机器名来静音某个主题。
- 要做: 原定并入 Setup: Notification Preferences and permission-set assignments are titled by raw user ids, not the person's name #24775,但该卡在裁决前已关闭。用户选择器已由 service-messaging: sys_notification_preference.user_id is a text column holding a user id or the sentinel '*', so the preference's title can only show the raw id; naming the person means re-typing it as a user reference (an accept-set narrowing) #24895 做完,订阅对象已退役;剩下的是主题和渠道改为可选列表。
- 完成标准: 新建偏好时从列表选主题和渠道。
Filing gate ③ a task the maintainer set: the maintainer's ruling on objectstack-ai/hotcrm#2194, recorded there at 6354968262 (the director seat, posting from the maintainer's account): 「25251 B hotcrm B」, 2026-10-23T01:05:47Z. That is letter B for all three rounds, exactly as the seat recommended, and it adopts T2-U15, ruled as a fold into #24775. That card closed completed on 2026-10-20T19:51Z, before the ruling, and its follow-ups landed part of this item but not all, so the remainder is filed here (see Why). Filed by the repo:hotcrm seat 1 (os-elon-musk, session session_014xanhPEmGTMTfVH4UokwJT) under objectstack-ai/hotcrm#2194's After the ruling. ⛔ Not a claim.
Grade: triage's. The seat suggests priority:p3: an adopted suggestion with a measured cost on an existing surface, no wrong data and no contract change.
Reader (who acts): objectstack triage routes and grades it. The landing below is the seat's reading of origin/main; paths under src/ are HotCRM's (objectstack-ai/hotcrm).
Governing text
- The ruling above. Per [Decision] Which of the 33 UI and UX suggestions from the 2026-10-20 browser passes to adopt (seat: adopt 23, fold 4 into open cards, defer 6) hotcrm#2194's own governing text, this item is an adopted suggestion through filing gate ②, not a defect card, and a platform or console landing is changed there, never re-implemented in HotCRM: 「平台相关的功能,应该尽量先改平台。」 (maintainer, 2026-10-18, ledger
6245607810).
Why (measured)
- Page / persona: Setup → Notification Preferences (and, in round 1, Subscriptions), the system administrator.
- Round 1: free-text "User*: Recipient user id, or '*'", "Principal: 'role:x' | 'team:x' | 'user:id' | …", and free-text topic and channel. Muting one user required pasting a 32-character id. Evidence: objectstack-ai/hotcrm track 2 report, UI and UX suggestions, U-15;
32-config-notification-preferences-new.png,33-config-notification-subscriptions-new.png. - What has landed since:
user_idis asys_userlookup (service-messaging: sys_notification_preference.user_id is a text column holding a user id or the sentinel '*', so the preference's title can only show the raw id; naming the person means re-typing it as a user reference (an accept-set narrowing) #24895, closed), so the person is picked;sys_notification_subscriptionis retired in 18 (packages/spec/src/migrations/entries/semantic/18.sys-notification-subscription-retired.ts). - What remains: Topic and Channel are still typed.
Landing
Read on objectstack origin/main 7b047b44:
packages/services/service-messaging/src/objects/notification-preference.object.ts:topic: Field.text("Notification topic, or '' for all topics.", ~:124) andchannel: Field.text("Channel id (inbox/email/push/…), or '' for all channels.", ~:140).- Channels are the registered
MessagingChannel.ids; topics are thesys_notification.topicvalues the platform and apps emit. The dev measures whether either set is enumerable today. Where none is, the field's help names where the values come from, and a picker over a set that does not exist is not built here.
Done when
- New Notification Preference offers Channel as a choice among the registered channels plus "all", and Topic as a choice among the known topics plus "all", wherever the platform can enumerate them.
- A typed value the platform cannot match stays refused or warned as today.
- Pins on the field declarations.
Size: M.
Dedupe
Method: every open objectstack-ai/objectstack issue (108) and every issue updated since 2026-10-19 (544, any state), listed through the repo-scoped issues endpoint (paged) at 2026-10-23T01:20Z and matched case-insensitively on their titles. Words: notification preference, topic, channel. 5 hits, all closed: #24775 and #24895 (the carrier and its landed follow-up, above), #25194, #24983 and #24924 (other messaging fields). New.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 23, 2026
Claim: card thread ·
domain:servicesseat 1 (seat post #6021, project run by the Sales account) · p2 · the whole card · 2026-10-23T12:51ZSession:
session_01GsN1y3qWPdkVLasbhDB2FyAccount:
os-sales(the seat's linked user; the card's assignee)Branch:
claude/project-thread-8hqiya(the harness-pinned branch for this session; it names no issue, so the Duplicate Fix Guard's branch-name hint will not match — this claim is the identity record)Worktree:
objectstack-issue-25346Domain:
domain:servicesSeat:
domain:services#1(seat post #6021)File surface (read on
origin/mainf0fdd45b; stop on a breach and explain it in the report):packages/services/service-messaging/src/objects/notification-preference.object.ts: thetopicandchannelfield declarations (the card's landing). The first act is the card's own measurement: whether either value set is enumerable today, and whether any field shape the platform declares can offer it as a picker. Where the shape of the answer is a public-contract choice (field type, stored wildcard spelling, an open channel seam), the dev stops and returnsneeds_decisioninstead of writing code.packages/services/service-messaging/src/translations/: regenerated withnode scripts/check-i18n-bundles.mjs --writewhen field help changes;zh-CNhand-translated.patchchangeset for@objectstack/service-messagingif code lands.packages/spec, notpackages/metadata-protocol(the identity-index migration), not objectui.Container & model:
M, card thread (in-session executor),model: default tier,effort: highClause-②: no
Metadata-protocol: none
Basis for the Clause-② line: provisional, for the help-text landing the card allows where no set is enumerable; if the ruling picks a closed picker it becomes
no (narrowing)and a superseding claim says so.Responsibility:
n/a — not a defect card(an adopted UX suggestion, filing gate ③)Thread-read: none
Serial constraints cleared: none (no open PR or in-flight claim declares
notification-preference.object.ts)Resume-from: none
Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
os-dev-report
{ "issue": 25346, "status": "needs_decision", "branch": "claude/project-thread-8hqiya", "pr": null, "session": "session_01GsN1y3qWPdkVLasbhDB2Fy", "premise_still_valid": true, "summary": "Measured on origin/main f0fdd45b, before any code. Topic: no enumerable set exists. Topics are free strings at each emit site (14 in platform code: ten approval.* in plugin-approvals, collab.mention, security.access_upgrade, auth.phone_otp, auth.phone_invite; plus any string an app's notify step writes), so per the card the help names where values come from. Channel: a runtime set exists (service-messaging registers inbox always, email and sms while their transport is resolvable; nobody registers push), but no declarable field shape offers it as a picker without a contract change: a select option value is a SystemIdentifierSchema (lowercase, 2+ chars), so the stored wildcard '*' cannot be an option; a select is closed on the write path, while the channel seam is open by contract (MessagingChannel is exported from the package index and MessagingService.registerChannel is public). Whether Channel counts as enumerable, and whether to change the contract for it, is the fork.", "tests": "none — no code written (needs_decision before code, per os-dev)", "gates": "none run — no diff", "dogfood_battery": "n/a — no diff", "mcp_calls": "4 — read tools only: issue_read get, issue_read get_comments (25346, 25280), search_pull_requests", "api_writes": "2 — via the fleet-write relay: label-write (assign os-sales, pm:queue replaced by pm:dispatched), post-stamped claim comment", "open_questions": [ { "question": "Channel: the platform knows its channels at runtime, but a picker needs a select, which cannot hold the '*' wildcard and would close the open MessagingChannel seam. Land help text, a closed select, or a spec-level suggestion list?", "options": [ "A — both fields stay text; the help names the sources (channel: inbox always, email with an email service, sms with an SMS service, '*' for all; drop the unimplemented push. topic: the topic a notify step or platform feature sends, e.g. approval.requested, '*' for all); pins on both declarations; bundles regenerated. Clause-② no, Metadata-protocol none, patch, about S.", "B — channel becomes a closed select over inbox/email/sms with the wildcard re-spelled (e.g. all): boot conversion of stored '*' rows, resolver and digest/quiet-hours help follow (B2: a service-owned notification_channel picklist that channel plugins extend). Clause-② no (narrowing), BREAKING, full dogfood, M to L. Closes the public channel seam for preferences and leaves two wildcard spellings in one row.", "C — a spec-lane decision to carry suggestion options on text fields (field.zod.ts notes importing the settings specifier's demote-to-suggestions semantics onto fields is a second ruling), rendered by objectui as a combobox; then service-messaging declares suggestions. Not this card's lane." ], "recommendation": "A, because the channel seam is open by contract and '*' cannot be a select value, so B trades a 3-value dropdown for a breaking migration and a second wildcard spelling; the long-term shape for open vocabularies is C, which is a spec decision with no other measured pull. The card itself provides for A where the platform cannot enumerate. If a real dropdown is wanted, file C on the spec lane rather than doing B here." } ], "out_of_scope_findings": [] }The four-axis analysis (Chinese) is with the services seat and the coordinator for the maintainer's batch.
Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
Ruling: human (Sales) · letter A on the needs_decision in os-dev-report 6365345478 · 2026-10-23T13:08Z
Ruled-by: Sales (
os-sales, the owner of the project that runs this lane), in the project chat at 2026-10-23T13:07:33Z, answering the coordinator's A/B/C summary of the report above. Verbatim: 「A」Recorded by: the card thread (session
session_01GsN1y3qWPdkVLasbhDB2Fy), via the fleet-write relay.What A means for this card (the report's option A, unchanged):
topicandchannelonsys_notification_preferencestaytext; no picker, no select, no stored-data change, nothing newly refused.channelhelp names the channel ids this deployment can send on:inboxalways,emailwhen an email service is configured,smswhen an SMS service is configured, and*for all channels. The unimplementedpushis dropped from the help.topichelp names where topics come from: the topic a notify step or a platform feature sends (for exampleapproval.requested), and*for all topics.patchchangeset for@objectstack/service-messaging.Clause-②: no,Metadata-protocol: none, as claim 6365308855 declared.Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
Landed ·
domain:servicesseat 1 (seat post #6021) ·os-sales·session_016g7nYP9wdNx1GDa5XDr7Uy· 2026-10-23T15:01Z6367059213), headad7975f4e5origin/main8878c36a58State: the card was closed by
Fixes #25346.pm:dispatchedis off and the assignee is cleared. The card thread's slot is free.Generated by Claude Code