Skip to content

fix(lark): oncall 群不再读访客消息额度,恒不限额 - #1217

Merged
deepcoldy merged 2 commits into
masterfrom
wt/botmux-claude-3-16-1
Sep 3, 2026
Merged

fix(lark): oncall 群不再读访客消息额度,恒不限额#1217
deepcoldy merged 2 commits into
masterfrom
wt/botmux-claude-3-16-1

Conversation

@deepcoldy

Copy link
Copy Markdown
Owner

messageQuota.defaultLimit 的语义是「授权卡 / 自助申请授权放进来的访客默认
额度」,但 oncall 群也在读它,导致三个连锁问题:

  • evaluateTalk 的放行腿是顺序短路的,oncall 腿排在 peer / teamBot 之前。oncall
    群 + 配了 defaultLimit 时,同团队 peer bot 被这条访客额度连带计量,本该让它
    「免 /grant 且不限额」的 teamBot 腿结构上不可达
  • 额度耗尽后 revokeQuotaGrantreason='oncall' 是 no-op(没有 chatGrant 可
    删),授权并未真被收回、计数器也不会清,于是每条后续消息都重发一张「额度已
    用尽」卡
  • 而恢复手段只有「点名带额度」/grant @x N:裸 /grant 只写 allowedChatGroups
    (同样被 oncall 短路)且不碰 quotaState,owner 反复授权无效。即「限了额也没法
    用 /grant 提额」,护栏在 oncall 场景下只会把群锁死。

因此 oncallTalk 改为恒不挂 quotaKey。访客侧不变:chatGrant / globalGrant 腿仍
各自挂 quotaKey 并照常消费 defaultLimit,非 oncall 群的过期授权照旧拒发 + 条件式
清理。会话群的存量兜底分支同步跟随(它本就复用 oncallTalk)。

同步收敛文案:dashboard 帮助文案与状态行、/botconfig 额度卡、bots.json 中英文档
过去都写着「授权卡与 Oncall 共用该额度」,现在明确为「只约束访客,Oncall 恒不限
额」。超上限值的兼容分支不再声称 oncall 按原值执行。

影响面

  • 会话类型:仅 oncall 群的额度判定。话题会话 / 普通群 / 私聊的 chatGrant、
    globalGrant、p2pOpen、allowedChatGroup 各腿逐字未改。
  • 跨 CLI / 跨后端:未触碰 adapters 与 PtyBackend / TmuxBackend,无差异。
  • 升级:存量配了 defaultLimit 的 bot,其 oncall 群转为不限额;已落盘的
    quotaState 记录成为孤儿(该群不再读它),不需要迁移也不会复活判定。

验证

  • npx tsc --noEmit 0 error;bun run build 通过。
  • 相关 10 个测试文件全绿(grant-gates / message-quota-enforcement /
    message-quota-doc-sync / dashboard-grant-defaults / dashboard-bot-defaults-layout /
    card-builder / card-handler-config / bot-talk-parity / grant-store /
    session-group-birth-quota)。
  • 反变异:把 oncall 读 defaultLimit 的旧实现原样接回,新增的 4 条守卫精确变红
    (grant-gates 的「oncall never carries a quotaKey」+ enforcement 的 3 条);
    把 bots.json 文档改回旧措辞,doc-sync 守卫变红。确认守卫有牙。
  • 新增守卫带阴性对照:同一个 defaultLimit 对非 oncall 群的 chatGrant 仍挂
    quotaKey,证明测的是「oncall 不读」而非「额度整体失效」。
  • 全量 --project unit 1191 passed / 7 failed,7 个红全部为本机环境既有问题:
    clean master 上逐一复现同样失败(pm2 / config-dir / mojo / plugin-mcp-sandbox /
    plugin-registry-sandbox-read 为 root+bwrap 环境类;worker-dsh 在 clean master
    上同样超时;tmux-pipe-backend-exit 单独跑 10/10 绿,属并发负载 flake)。

⚠️ 未做真实飞书 e2e:上报该问题的两个 bot 不在本机。

🤖 Generated with Claude Code

`messageQuota.defaultLimit` 的语义是「授权卡 / 自助申请授权放进来的**访客**默认
额度」,但 oncall 群也在读它,导致三个连锁问题:

- `evaluateTalk` 的放行腿是顺序短路的,oncall 腿排在 peer / teamBot 之前。oncall
  群 + 配了 defaultLimit 时,同团队 peer bot 被这条访客额度连带计量,本该让它
  「免 /grant 且不限额」的 teamBot 腿**结构上不可达**。
- 额度耗尽后 `revokeQuotaGrant` 对 `reason='oncall'` 是 no-op(没有 chatGrant 可
  删),授权并未真被收回、计数器也不会清,于是**每条后续消息都重发一张「额度已
  用尽」卡**。
- 而恢复手段只有「点名带额度」`/grant @x N`:裸 `/grant` 只写 allowedChatGroups
  (同样被 oncall 短路)且不碰 quotaState,owner 反复授权无效。即「限了额也没法
  用 /grant 提额」,护栏在 oncall 场景下只会把群锁死。

因此 `oncallTalk` 改为恒不挂 quotaKey。访客侧不变:chatGrant / globalGrant 腿仍
各自挂 quotaKey 并照常消费 defaultLimit,非 oncall 群的过期授权照旧拒发 + 条件式
清理。会话群的存量兜底分支同步跟随(它本就复用 oncallTalk)。

同步收敛文案:dashboard 帮助文案与状态行、/botconfig 额度卡、bots.json 中英文档
过去都写着「授权卡与 Oncall 共用该额度」,现在明确为「只约束访客,Oncall 恒不限
额」。超上限值的兼容分支不再声称 oncall 按原值执行。

## 影响面

- 会话类型:仅 oncall 群的额度判定。话题会话 / 普通群 / 私聊的 chatGrant、
  globalGrant、p2pOpen、allowedChatGroup 各腿逐字未改。
- 跨 CLI / 跨后端:未触碰 adapters 与 PtyBackend / TmuxBackend,无差异。
- 升级:存量配了 defaultLimit 的 bot,其 oncall 群转为不限额;已落盘的
  `quotaState` 记录成为孤儿(该群不再读它),不需要迁移也不会复活判定。

## 验证

- `npx tsc --noEmit` 0 error;`bun run build` 通过。
- 相关 10 个测试文件全绿(grant-gates / message-quota-enforcement /
  message-quota-doc-sync / dashboard-grant-defaults / dashboard-bot-defaults-layout /
  card-builder / card-handler-config / bot-talk-parity / grant-store /
  session-group-birth-quota)。
- 反变异:把 oncall 读 defaultLimit 的旧实现原样接回,新增的 4 条守卫精确变红
  (grant-gates 的「oncall never carries a quotaKey」+ enforcement 的 3 条);
  把 bots.json 文档改回旧措辞,doc-sync 守卫变红。确认守卫有牙。
- 新增守卫带阴性对照:同一个 defaultLimit 对**非 oncall** 群的 chatGrant 仍挂
  quotaKey,证明测的是「oncall 不读」而非「额度整体失效」。
- 全量 `--project unit` 1191 passed / 7 failed,7 个红全部为本机环境既有问题:
  clean master 上逐一复现同样失败(pm2 / config-dir / mojo / plugin-mcp-sandbox /
  plugin-registry-sandbox-read 为 root+bwrap 环境类;worker-dsh 在 clean master
  上同样超时;tmux-pipe-backend-exit 单独跑 10/10 绿,属并发负载 flake)。

⚠️ 未做真实飞书 e2e:上报该问题的两个 bot 不在本机。
复审指出用户面文案改全了,但**代码注释漏了 6 处**:它们仍在描述「oncall 会按
messageQuota.defaultLimit 挂 quotaKey」这个已不可能发生的语义,与同文件新的
`oncallTalk` 注释直接矛盾。本仓注释是承重资产,说谎的注释比没有注释更危险。

- `event-dispatcher.ts` — `TalkEvaluation.explicitGrantOverride` /
  `expiredGrantCleanup` 两个字段注释:改为标明**已无生产产出方**,保留历史语义
  说明与「为何保留字段」。
- `daemon.ts` — 交集决策注释块:原先描述的三个分支(explicitGrantOverride /
  expiredGrantCleanup / 其余 oncall 兜 default)在生产中已全部不可达。改为说明
  `def` 恒为 undefined 的理由:只有 chatGrant / globalGrant 挂 quotaKey,带
  `reason==='oncall'` 的判定在 `if (!ev.quotaKey) return true` 就已返回。
- `bot-registry.ts` — `messageQuota` 字段注释删掉「同时作为 oncall 默认额度」。
- `dashboard-ipc-server.ts` — `messageQuotaDefaultLimit` 的路由注释同上。
- `grant-store.ts` — `consumeQuota` 的懒初始化与 `expiredGrant` 文档:标明两者
  当前都没有生产调用方(访客额度记录是**发卡那一刻**按 `defaultLimit ?? 3` 写好
  的,不依赖懒初始化),参数保留仅为不改签名。
- `session-groups-store.ts` — `originReason` 注释里「a FRESH per-group allowance
  when one is」已不再成立。

顺带修一处**误导性标识符**(自查发现,非复审提出):dashboard 兼容分支的 i18n
占位符 `{oncallCount}` 现在承载的是「超过上限的配置值」而不是「oncall 的额度」,
与被清理的那些注释属同一类问题,重命名为 `{configuredCount}`(4 处文案 + 2 处
调用点,中英同步)。

不删死机器:`consumeQuota` 签名、`TalkEvaluation` 的两个字段、以及 grant-store
的相关测试留作独立 cleanup —— 本次只保证注释不再描述已死语义。

## 验证

- `npx tsc --noEmit` 0 error;`bun run build` 通过。
- 相关 10 个测试文件全绿(322 条)。
- 反变异复跑:把 `oncallTalk` 改成挂 quotaKey ⟹ grant-gates 与
  message-quota-enforcement 的 5 条守卫精确变红;还原后复绿。确认本次注释改动
  没有削弱行为守卫。
@deepcoldy
deepcoldy merged commit ae72b2c into master Sep 3, 2026
8 of 9 checks passed
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

🚀 Released in v3.18.14

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant