self_recall/edit平台撤回窗口+per-chat速率预算 #120

Merged
Yushu merged 2 commits from feat/115-recall-window-and-rate-budget into main 2026-08-12 05:13:00 +00:00
Member

Closes #115

Summary

ADR-0012 §6 的两根安全支柱之一——速率预算(主兜底)——此前从未落地,另一根
(平台撤回窗口)依赖的 Sent Log 时间字段也从未补上。本片补齐两者,顺带吸收
send_messagequote_message_id 跨 chat 引用校验缺口。

  • Sent Log 加 sent_at 时间字段SentLogEntry/SentLogModel/
    StoragePort.record() 协议及内存/持久化两个实现均适配;None 显式
    fail-closed(视同已超窗口),不随包分发迁移(可空列)。
  • 平台撤回窗口硬约束:新增 _within_platform_recall_window 纯函数 +
    platform_recall_window_seconds 独立构造参数(因被 _handle_self_recall/
    _handle_edit 两个方法读取,按既有 RuntimeLoopConfig 归类规则不进
    config);self_recall/edit 在能力位分叉之前拒绝超窗口句柄,零平台调用。
  • per-chat 动作速率预算:新模块 action_rate_budget.py,形状仿
    gating.SilenceBudgetState 令牌桶先例但不复用;self_recall/edit
    合计消费同一个 per-chat 桶,降级 recall+resend 路径的 egress.recall
    同样受限;内存 + 持久化两个存储实现 + 新表。
  • quote_message_id 跨 chat 校验(顺带吸收):_run_tool_loop 新增
    known_message_ids 参数,只有 run() 真实来信时非空,事件驱动路径一律
    降级为不带引用的普通发送。

Review

两轴 review(sonnet,Standards + Spec 轴并行,随后对抗式复核)抓到 4 条
standards 轴发现,全部确认成立并已修复:

  • HIGH_handle_edit 降级路径的 self_recall 能力检查/MAX_SENDS_PER_TURN
    检查排在预算消费之后,导致注定打不到平台的失败请求白白吃掉预算、饿死同
    chat 后续真正合法的 self_recall/edit——已复现、修复、补两条回归测试,并
    用变异测试验证修复确实被新测试捕获。
  • MEDIUMtest_handle_ownership.py 文档字符串过期(声称 SentLogEntry
    仍无 time 字段)——已订正。
  • LOWPersistentStorage 的速率预算读写此前只测过内存假实现——补了
    DB round-trip 测试(test_action_rate_budget_storage.py)。
  • LOW:撤回窗口边界(elapsed 恰好等于 window_seconds)没有测试覆盖——
    补了纯函数边界单测(test_platform_recall_window.py)。

Spec 轴零发现——AC 四条逐项核对无遗漏。

Test plan

  • uv run pytest:1631 passed
  • uv run ruff check .:clean
  • 变异测试验证所有新守卫(窗口/预算/quote校验,10 处逐一变异验红后恢复)
  • 两轴 review(sonnet)+ 对抗式复核,4 条发现全部处理
  • ADR-0012 更新节已在 docs 分支本地提交(不推送)
Closes #115 ## Summary ADR-0012 §6 的两根安全支柱之一——速率预算(主兜底)——此前从未落地,另一根 (平台撤回窗口)依赖的 Sent Log 时间字段也从未补上。本片补齐两者,顺带吸收 `send_message` 的 `quote_message_id` 跨 chat 引用校验缺口。 - **Sent Log 加 `sent_at` 时间字段**:`SentLogEntry`/`SentLogModel`/ `StoragePort.record()` 协议及内存/持久化两个实现均适配;`None` 显式 fail-closed(视同已超窗口),不随包分发迁移(可空列)。 - **平台撤回窗口硬约束**:新增 `_within_platform_recall_window` 纯函数 + `platform_recall_window_seconds` 独立构造参数(因被 `_handle_self_recall`/ `_handle_edit` 两个方法读取,按既有 `RuntimeLoopConfig` 归类规则不进 config);`self_recall`/`edit` 在能力位分叉之前拒绝超窗口句柄,零平台调用。 - **per-chat 动作速率预算**:新模块 `action_rate_budget.py`,形状仿 `gating.SilenceBudgetState` 令牌桶先例但不复用;`self_recall`/`edit` **合计**消费同一个 per-chat 桶,降级 recall+resend 路径的 `egress.recall` 同样受限;内存 + 持久化两个存储实现 + 新表。 - **quote_message_id 跨 chat 校验**(顺带吸收):`_run_tool_loop` 新增 `known_message_ids` 参数,只有 `run()` 真实来信时非空,事件驱动路径一律 降级为不带引用的普通发送。 ## Review 两轴 review(sonnet,Standards + Spec 轴并行,随后对抗式复核)抓到 4 条 standards 轴发现,全部确认成立并已修复: - **HIGH**:`_handle_edit` 降级路径的 self_recall 能力检查/`MAX_SENDS_PER_TURN` 检查排在预算消费之后,导致注定打不到平台的失败请求白白吃掉预算、饿死同 chat 后续真正合法的 self_recall/edit——已复现、修复、补两条回归测试,并 用变异测试验证修复确实被新测试捕获。 - **MEDIUM**:`test_handle_ownership.py` 文档字符串过期(声称 `SentLogEntry` 仍无 time 字段)——已订正。 - **LOW**:`PersistentStorage` 的速率预算读写此前只测过内存假实现——补了 DB round-trip 测试(`test_action_rate_budget_storage.py`)。 - **LOW**:撤回窗口边界(elapsed 恰好等于 window_seconds)没有测试覆盖—— 补了纯函数边界单测(`test_platform_recall_window.py`)。 Spec 轴零发现——AC 四条逐项核对无遗漏。 ## Test plan - [x] `uv run pytest`:1631 passed - [x] `uv run ruff check .`:clean - [x] 变异测试验证所有新守卫(窗口/预算/quote校验,10 处逐一变异验红后恢复) - [x] 两轴 review(sonnet)+ 对抗式复核,4 条发现全部处理 - [x] ADR-0012 更新节已在 `docs` 分支本地提交(不推送)
ADR-0012 §6 治理主兜底:self_recall/edit 补上平台撤回窗口硬约束
(sent_at 缺省 fail-closed)与 per-chat 速率预算(新模块
action_rate_budget.py,形状仿 gating.SilenceBudgetState 先例但不复用);
顺带吸收 quote_message_id 跨chat引用校验(known_message_ids 只在 run()
真实来信时非空,事件驱动路径一律降级为不带引用)。
- _handle_edit 降级路径的 self_recall 能力检查/MAX_SENDS_PER_TURN 检查此前排
  在速率预算消费之后,导致注定打不到平台的失败请求白白吃掉预算、饿死同 chat
  后续真正合法的 self_recall/edit(HIGH,两轴 review 抓到,已复现+回归测试
  锁定,变异验证过修复确实被新测试捕获)。
- 修正 test_handle_ownership.py 过期文档:"SentLogEntry 至今没有 time 字段"
  在本 issue 落地后已是假命题(MEDIUM)。
- 补 PersistentStorage.get_action_rate_budget/update_action_rate_budget 的
  真实 DB round-trip 测试,此前只测过内存假实现(LOW)。
- 补 _within_platform_recall_window 的边界单测(elapsed 恰好等于 window_
  seconds),handler 级 e2e 用例受墙钟时间流逝限制测不到这个点(LOW)。
Yushu merged commit 34ce787ce7 into main 2026-08-12 05:13:00 +00:00
Yushu deleted branch feat/115-recall-window-and-rate-budget 2026-08-12 05:13:01 +00:00
Sign in to join this conversation.
No description provided.