句柄归属限当前会话 + 补上习得贴纸的开关命令 #107
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/102-handle-ownership-and-sticker-toggle"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #102
两条都是全 ADR 查漏那 8 条「高紧迫」里从分类里掉出去的(既没进任何票、也没进任何清单)。
1.
self_recall/edit可用 A 群的句柄操作 B 群的消息chat 作用域出自 ADR-0012 §2 触发(「撤/改近期已发消息(限当前会话 + 平台撤回窗口)」);§6 治理的「硬约束(结构不变量)」给的是整组约束的 fail-closed 精神(「句柄必须解析,否则工具失败不动(fail-closed:误撤比不撤更糟)」)。而这两个函数拿到
entry后从不比较entry.chat_id与本轮chat_id—— 查漏方已实测「在 gB 群用 gA 群的句柄撤回成功」。风险不在「句柄被猜到」(uuid4 前 12 位跨会话猜不到),而在模型记错归属:同一模型在多个 chat 间轮转,句柄形如
m7的短标识极易串。既有测试为什么抓不到:
test_self_message_control.py全部用例都在单会话内 —— 典型的「测试与被测守卫同构,守卫不存在也全绿」。新增test_handle_ownership.py,每条用例都跨两个 chat(那是唯一能触达这道闸的形状),并额外断言状态也没被改(否则 A 群那条会变成「已撤回」,之后 A 群自己想撤就撤不了了)。闸放在能力位分叉之前:无原生 edit 能力时这条路降级成「撤回 + 重发」,第一步就是
egress.recall—— 放在分叉之后等于给降级路径开后门。有单独用例钉住这个顺序,代码现场也写了这句取舍。范围:§2 那句里的「平台撤回窗口」那一半不在本票 —— ADR-0012 §7 本来就把 Sent Log 定义成带
time字段,而SentLogEntry至今没有它,补它属第四批(#103)。常量 docstring 里写明了。同族还有一处未闭合也一并记下:send_message的quote_message_id是模型直给的平台 id,不经 Sent Log、没有chat_id可比,这道闸射程外。2. 习得贴纸的 opt-in 开关根本没有命令可以打开
ADR-0024 决策第 4 条逐字:「默认关闭(opt-in),per-chat 开关:心智模型同既有
/settool(CONTEXT.md 管理命令)」。set_sticker_learning_enabled在 issue #70 就建好了,但src/里零调用点 —— 于是识别 → 去重 → 人设漂移守护 → 落库整条链永不触发、search_stickers恒查空库。249 行learned_sticker.py+ 一张表 + 一个 Qdrant collection + 工具 schema + 一整套测试,产出为零。 那份文档把跟进挂在「ADR-0010 运营面建好后」,而运营面在 #83 就宣布全部落地了,只是没人回来接这根线。补上
/setsticker on|off(第八条管理命令,走ports.is_admin)。issue #88 建的穷举守卫按预期变红了 —— 那正是它该做的;新命令因此自动获得了全套边界用例(force_whitespace、粘连后缀等)。端到端断言走的是模型真正会用的那个出口 ——
search_stickers工具本身,不是存储里那个布尔、也不是存储层的检索方法。布尔为真而链路不通、或落库了但工具查不出来,这条用例都会红。没有贴纸能力的平台上不假装开启(两轴 review 抓到的):
_learn_stickers第一道闸就是capabilities.sticker,而CapabilitySet全字段默认False—— 照写照回「好,开始学了」就是本仓反复点名的那个反模式(admin.py/subagent.py都写着「命令就承诺了它没兑现的事」)。改成拒绝 + 说明 + 不写库(存一个恒不生效的True只会让下一个排查的人以为开关是好的);关闭方向不设这道闸 —— 能力位可能刚从真变假而记录还留着。顺带把
on|off抽成admin.parse_on_off给两条命令共用(对/settool行为等价),并配一条把两者钉在一起的守卫:分两份实现的话,「大小写不敏感是刻意的」这句取舍只写在一处,另一处很容易被写成大小写敏感,而那种差异要等管理员打了ON才暴露。开启的回复如实说清在采集什么(他人发的内容、未经其同意,ADR-0024 明确拒绝了 opt-out),有断言钉住。
两轴 review 抓到的(第二个 commit)
is False,而False本就是默认值 —— 分不出「没写」和「写了 False」。改成先置True再打命令。_native_edit_egress()从文件底部 + 函数内 import 挪到顶部。已知未做
time字段,属 #103 第四批)。quote_message_id的同族缺口(模型直给的平台 id,无从校验归属)。/setsticker—— 已随 ADR 回填一并做(docs 分支本地提交)。验证
uv run pytest -n auto→ 1537 passed(新增 36)。ruff check全过。变异复验(每次都先核实变异真写进文件):self_recall 不查归属 → 2 红;edit 不查归属 → 3 红;开关命令不写存储 → 2 红;开关命令不查权限 → 1 红;on/off 改成大小写敏感 → 3 红;去掉能力位拒绝 → 1 红;edit 归属闸挪到分叉之后 → 3 红。还原后各文件 md5 与备份一致。