Repository navigation
sms settings 的 provider options 表与 SMS transports 之间没有契约测试 —— mail 有,sms 没有(#5094 同形,目前两端恰好一致) #5773
Description
Activity
分诊(发现分诊轮,#4949 纪律):持有(留
finding),补域标签domain:services。- 域锚定:两端都在 services —— manifest 侧
packages/services/service-settings/src/manifests/sms.manifest.ts,transport 侧packages/services/service-sms/src/transports/index.ts;按域表packages/services/*⇒domain:services。(mail 的对照物mail-manifest-providers.contract.test.ts住在plugin-email,按域表同属 services 行,所以无论测试最终落哪一边,车道都不变 —— 这也是不必为「测试住哪个包」另开决策卡的原因。) - 为什么还留着(不晋级):① 今日两端集合恰好相等(
log/aliyun/twilio),无用户可达缺陷,正文自陈是休眠的漂移风险;② 更硬的一条 —— 这道契约测试的原料SMS_TRANSPORT_PROVIDERS具名导出尚未在 main 上,它由 PR fix(cli,service-sms)!: 启动期拒绝表外的 OS_SMS_PROVIDER,而不是静默降级成 LogSmsTransport (#5713) #5771(CLI 启动直读 process.env.OS_SMS_PROVIDER / OS_EMAIL_PROVIDER,完全绕过 settings 的 options 表 —— #5204 的闸门看不到这条路 #5713,cli 车道在飞,本轮复核 state=open、未合并) 引入。现在派发会与在飞 PR 争同一批文件。 - 重启条件:PR fix(cli,service-sms)!: 启动期拒绝表外的 OS_SMS_PROVIDER,而不是静默降级成 LogSmsTransport (#5713) #5771 合入 main 后晋级
pm:queue。晋级时,正文那个未决点(测试住service-sms需新增对service-settings的依赖、还是住service-settings反向 importservice-sms,取决于依赖方向是否成环)作为实施内第一步判定即可,不另开决策卡 —— 它是可由代码判定的技术问题,不是产品拍板。 - 关联已核:plugin-email: SendGrid / Amazon SES 设置项同样后端无实现 —— #5087 的同形缺口 #5094(mail 侧同形漂移的原始案例,以及那道双向契约测试的来源)、service-settings: select 型 specifier 的 options 在保存期完全不校验 —— 声明的枚举不被强制 #5131(options 表在写入路径上的执行)、CLI 启动直读 process.env.OS_SMS_PROVIDER / OS_EMAIL_PROVIDER,完全绕过 settings 的 options 表 —— #5204 的闸门看不到这条路 #5713 / PR fix(cli,service-sms)!: 启动期拒绝表外的 OS_SMS_PROVIDER,而不是静默降级成 LogSmsTransport (#5713) #5771(不同的面:构造期 env 拒收,不是 settings 下拉框)。
本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
- 域锚定:两端都在 services —— manifest 侧
分诊(发现分诊轮):重启条件已触发 ⇒ 晋级
pm:queue(域标签维持domain:services)上一轮(2026-08-06 04:58Z)判持有,记的重启条件是「PR #5771 合入后 —— 该 PR 提供的具名导出是这道测试的原料」。本轮核验该条件已满足:
- PR fix(cli,service-sms)!: 启动期拒绝表外的 OS_SMS_PROVIDER,而不是静默降级成 LogSmsTransport (#5713) #5771 已 MERGED,
merged_at= 2026-08-06 05:02:03Z(basef205c32)。 origin/main(1624f4a)上原料到位:SMS_TRANSPORT_PROVIDERS = ['log','aliyun','twilio'] as const(packages/services/service-sms/src/transports/index.ts:27)、isSmsTransportProvider()(:38),并经packages/services/service-sms/src/index.ts:13-14出包;SmsProviderTag已从数组派生(不再是第二份字面量)。- 另一端仍是正文描述的形状:
packages/services/service-settings/src/manifests/sms.manifest.ts:24的provider仍是带 options 表的select,sms.manifest.test.ts仍只断言 schema/权限/密钥字段,没有任何测试比对取值集合。两端「恰好一致」的休眠漂移风险原样成立。
落点与待决取舍(留给派发词,不是拍板项): 双向比对
sms.manifest.ts的 options 与SMS_TRANSPORT_PROVIDERS;测试住哪一侧(service-sms新增对service-settings的依赖——需先确认不成环;或service-settings反向 importservice-sms)由 dev 在实读依赖图后定,mail 侧的既有形是packages/plugins/plugin-email/src/mail-manifest-providers.contract.test.ts(住在消费侧插件里),可作方向参考。查重:三仓 open issue 与 PR 各搜一遍,无影子单;#5713/#5094/#5131 均为相关面而非重复。
ℹ️ 提示:
domain:services座位自 2026-08-06 05:15Z 起按维护者指示暂停(见 #4604 该行),本单入队后需等座位恢复才会被选批。本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
- PR fix(cli,service-sms)!: 启动期拒绝表外的 OS_SMS_PROVIDER,而不是静默降级成 LogSmsTransport (#5713) #5771 已 MERGED,
认领:PM 循环第 3 轮(services 车道)
会话:session_015a5qkLzpGXhLL2F5gvJ7dD
分支:claude/issue-5773-sms-manifest-provider-contract
Worktree:objectstack-issue-5773
域:domain:services
文件面:新契约测试一份(住packages/services/service-sms或packages/services/service-settings,依赖方向 dev 实读后定,mail 先例住消费侧)+sms.manifest.ts如需加指向注释;⛔ 不改 provider 词表本身
串行约束已清:PR #5771(原料SMS_TRANSPORT_PROVIDERS)已合于 05:02Z,今日 de770bf 触过 service-sms,基于合并后 origin/main;#2814(将增 sms settings 键、同 manifest 文件)后轮串行;#5712 服务半边 blocked(#5933),同包无在飞冲突
Generated by Claude Code
验收:ACCEPT → PR #5982(tests-only;CI 绿后转 ready 入队,本座位跟到 MERGED)。
- 双向契约测试照 mail 先例落地且更进一步:⊆/⊇ 两方向独立成
it各自点名漂移值;第二层断言正确的 transport 类(非「没抛异常」);isSmsTransportProvider(CLI 启动直读 process.env.OS_SMS_PROVIDER / OS_EMAIL_PROVIDER,完全绕过 settings 的 options 表 —— #5204 的闸门看不到这条路 #5713 CLI 拒收判据)与下拉框锁同词表;默认值log在表内且可构建。 - 探针 B 的预测落空是本单最有价值的产出:照抄 mail 先例的三行
toBeInstanceOf被实测证明是「词表第三份拷贝」反模式(删项重复报红、加项静默不覆盖)→ 改为PROVIDER_FIXTURES表 + 键集 ≡SMS_TRANSPORT_PROVIDERS覆盖断言,且刻意用运行时断言避开@ts-expect-error退役 pin 在packages/spec里是幽灵检查:tsconfig 把**/*.test.ts排除出唯一的tsc --noEmit#5286 的 phantom-check 形状(本包 tsconfig 排除测试文件,类型层穷尽无 tsc 求值)。mail 侧同形反模式值得留意(未立单 —— 它的三行在被 tsc 求值的包里,危害减半)。 - 落点判定合格:实测两向不成环后按架构理由选 transport owner 持测试(注册表不反向 import 各 provider 实现),与 mail 先例同向;判定理由写进 PR。
- TEST_DEBT 台账重测量(3→4,errors 不动)带实测证据,
sms.manifest.tsCONTRACT 注释指名测试路径与两方向 —— 一致性半径内的必要 rider。 - 必答项两条:feat(sms): 短信全局/每租户日发送配额(成本总量闸) #2814 完全无影响(断言面 scope 到
key === 'provider',并在 TSDoc 里点名 feat(sms): 短信全局/每租户日发送配额(成本总量闸) #2814 写死该约束 —— 结构性隔离而非口头承诺);spec: SettingsManifest 的 SpecifierSchema 新增valueDomain闭合枚举 —— 声明存在时标准域为执行边界,options 退化为 UI 便利列表(#5712 裁决的 spec 半边) #5933 无影响(其正文自陈注册表背书封闭集照旧)。 skip-changeset已由 dev 以读回并集方式打上(bot 标签零覆盖);Check Changeset 首跑竞态红已按 fix(ci): Check Changeset 实时读 skip-changeset 标签,首个 run 不再永久红 (#5580) #5625 语义 rerun 翻绿。
经办:services 座位,会话
session_015a5qkLzpGXhLL2F5gvJ7dD(第 3 轮)。
Generated by Claude Code
- 双向契约测试照 mail 先例落地且更进一步:⊆/⊇ 两方向独立成
- added a commit that references this issue
on Aug 7, 2026
在 #5713(CLI 启动期拒绝表外
OS_SMS_PROVIDER,PR #5771)中发现,记录下来交 triage。不在 #5713 的完成范围内 —— #5713 修的是os serve的构造期读取路径,这条是 settings 下拉框与 transports 之间的一致性,两个不同的面。事实
mail 侧有一道可执行的契约闸门,sms 侧没有对应物。
packages/services/service-settings/src/manifests/mail.manifest.ts:5-28的文件头把PROVIDER_OPTIONS明确称为「a CONTRACT, not a menu of aspirations」,并指向执行它的测试:packages/plugins/plugin-email/src/mail-manifest-providers.contract.test.ts—— 它把 manifest 里的取值与EMAIL_TRANSPORT_PROVIDERS/makeTransport双向比对,任一方向漂移即红。packages/services/service-settings/src/manifests/sms.manifest.ts:24-29的provider是同一形状的select(log/aliyun/twilio),但仓库里没有任何测试把它与makeSmsTransport能建的 tag 集合比对。sms.manifest.test.ts只断言 manifest 能通过SettingsManifestSchema、命名空间的权限、以及两个密钥字段是password+encrypted,不涉及取值集合。grep 佐证:
smsSettingsManifest的全部引用只在service-settings包内部(index.ts/manifests/index.ts/sms.manifest.ts/sms.manifest.test.ts),没有任何 transport 侧的消费者。为什么算一类问题
这正是 #5094 已经付过一次学费的形状:mail 的下拉框曾提供
sendgrid/ses而背后没有 transport(选中、校验通过、保存成功、然后什么都不发),同时真正有 transport 的resend根本选不到。修法是让两边由一个词汇表 + 一道契约测试锁死。sms 现在有两份独立维护的字面量,只是恰好一致,没有任何东西在它们分开时报警。PR #5771 把 sms transports 的词汇表提成了具名导出
SMS_TRANSPORT_PROVIDERS(packages/services/service-sms/src/transports/index.ts),所以写这道契约测试的原料现在是现成的:比对sms.manifest.ts的provideroptions 与SMS_TRANSPORT_PROVIDERS,双向。严重度(诚实标注)
今天没有用户会撞上 —— 两端都是
log/aliyun/twilio,集合相等。这是一条休眠的漂移风险,不是现存缺陷,所以按 observation-class 打finding标签、不入pm:queue,请 triage 分级。一个已知的落地障碍:mail 的契约测试住在
plugin-email里(它依赖service-settings)。sms 的对应测试要么住在service-sms(需要新增对service-settings的依赖 —— 应先确认不成环),要么住在service-settings里反向 importservice-sms。选哪边是这条 issue 需要决定的事情之一。相关