人设漂移守护判断留痕+安全基线补编造身份禁令 #121

Merged
Yushu merged 2 commits from feat/116-drift-guard-trace-and-identity-baseline into main 2026-08-12 07:48:38 +00:00
Member

Closes #116

Summary

两条独立的守护类缺口,均零前置、零依赖,合成一片:

  • 人设漂移守护判断留痕:ADR-0021 拍板的"判断结论带简短理由(供
    /why)"与"判断失败记入 /why 标注'判断失败'"两条 ADOPTED 条款此前
    一件都没落地——五处调用点(反思闭环画像/技能候选、Delta 压缩 Knowledge
    候选、习得贴纸、跨平台动态拉取)拿到 DriftVerdict 后只读
    .status == "pass",判否/判断失败直接丢弃,候选被否决时运维和用户看不到
    任何痕迹。
    • 新增 drift_veto.pyDriftVetoRecord/存储 port 切片(内存+持久化)+
      judge_and_maybe_veto/record_veto_if_needed 两个组合入口,per-chat
      保留环形状仿 decision_snapshot.py 但独立实现。
    • reflection._commit_lesson 补上了此前缺失的 chat_id 参数。
    • cross_platform_pull.pull_cross_platform_profile 本身不写存储(可能被
      抢占取消),返回类型改为 PullOutcome{candidate, verdict},由调用方
      entry_drive_tick._run_cross_platform_pull 按需落痕。
    • /why 追加渲染这条留痕(explain.render_drift_vetoes + 人话表穷举
      守卫,同既有惯例)。
  • 安全基线补编造身份禁令SECURITY_BASELINE 追加第五条 bullet
    ("不得编造 persona/backstory/Knowledge/Persona Lore 之外的核心身份类
    事实"),不新增 port/存储/子系统/config,纯文本追加;补一条非空转的语义
    断言测试(不只是断言常量被注入到既有三处消息构造点)。

Review

两轴 review(sonnet,Standards + Spec 轴并行,随后对抗式复核)抓到 3 条
确认成立的发现,全部已修复;1 条 low 严重度发现经复核判定不成立(有具体
理由,未采纳):

  • medium(两轴独立抓到同一根因)cross_platform_pull 判否后没有走
    judge_and_maybe_veto,而是在 entry_drive_tick.py 里手写了一份等价的
    if verdict.status != "pass" 判断,与模块文档"唯一入口"的表述矛盾——
    已拆出 record_veto_if_neededjudge_and_maybe_veto 的后半段)给
    entry_drive_tick.py 复用,"该不该留痕"判定收敛回一份代码,同步订正
    两处失实文档措辞,补一条等价性回归测试。
  • lowPullOutcome 去 Optional 化后 asyncio.gather 的类型推断失效,
    basedpyright 报 2 个新错(不挡 CI 实际用的 ty,但影响本地 IDE 体验)
    ——改用显式 list[asyncio.Task[Any]] 收集后再 gather,basedpyright
    验证归零。
  • low,未采纳/why 理论上"有留痕但无决策快照"会提前返回、看不到
    留痕——复核逐一追了五处留痕调用点的触发前提,确认当前代码结构下这个
    场景不可达(每条留痕产生路径都必然先经过一次会写决策快照的门控评估),
    判定不成立。

Test plan

  • uv run pytest:1688 passed
  • uv run ruff check .:clean
  • uv run basedpyright src/nonebot_plugin_arise/entry_drive_tick.py:0 errors
  • 变异测试验证所有新守卫(8 处逐一变异验红后恢复)
  • 两轴 review(sonnet)+ 对抗式复核,3 条确认发现全部处理
  • ADR-0021/0022 更新节已在 docs 分支本地提交(不推送)
Closes #116 ## Summary 两条独立的守护类缺口,均零前置、零依赖,合成一片: - **人设漂移守护判断留痕**:ADR-0021 拍板的"判断结论带简短理由(供 `/why`)"与"判断失败记入 `/why` 标注'判断失败'"两条 ADOPTED 条款此前 一件都没落地——五处调用点(反思闭环画像/技能候选、Delta 压缩 Knowledge 候选、习得贴纸、跨平台动态拉取)拿到 `DriftVerdict` 后只读 `.status == "pass"`,判否/判断失败直接丢弃,候选被否决时运维和用户看不到 任何痕迹。 - 新增 `drift_veto.py`:`DriftVetoRecord`/存储 port 切片(内存+持久化)+ `judge_and_maybe_veto`/`record_veto_if_needed` 两个组合入口,per-chat 保留环形状仿 `decision_snapshot.py` 但独立实现。 - `reflection._commit_lesson` 补上了此前缺失的 `chat_id` 参数。 - `cross_platform_pull.pull_cross_platform_profile` 本身不写存储(可能被 抢占取消),返回类型改为 `PullOutcome{candidate, verdict}`,由调用方 `entry_drive_tick._run_cross_platform_pull` 按需落痕。 - `/why` 追加渲染这条留痕(`explain.render_drift_vetoes` + 人话表穷举 守卫,同既有惯例)。 - **安全基线补编造身份禁令**:`SECURITY_BASELINE` 追加第五条 bullet ("不得编造 persona/backstory/Knowledge/Persona Lore 之外的核心身份类 事实"),不新增 port/存储/子系统/config,纯文本追加;补一条非空转的语义 断言测试(不只是断言常量被注入到既有三处消息构造点)。 ## Review 两轴 review(sonnet,Standards + Spec 轴并行,随后对抗式复核)抓到 3 条 确认成立的发现,全部已修复;1 条 low 严重度发现经复核判定不成立(有具体 理由,未采纳): - **medium(两轴独立抓到同一根因)**:`cross_platform_pull` 判否后没有走 `judge_and_maybe_veto`,而是在 `entry_drive_tick.py` 里手写了一份等价的 `if verdict.status != "pass"` 判断,与模块文档"唯一入口"的表述矛盾—— 已拆出 `record_veto_if_needed`(`judge_and_maybe_veto` 的后半段)给 `entry_drive_tick.py` 复用,"该不该留痕"判定收敛回一份代码,同步订正 两处失实文档措辞,补一条等价性回归测试。 - **low**:`PullOutcome` 去 Optional 化后 `asyncio.gather` 的类型推断失效, basedpyright 报 2 个新错(不挡 CI 实际用的 `ty`,但影响本地 IDE 体验) ——改用显式 `list[asyncio.Task[Any]]` 收集后再 gather,basedpyright 验证归零。 - **low,未采纳**:`/why` 理论上"有留痕但无决策快照"会提前返回、看不到 留痕——复核逐一追了五处留痕调用点的触发前提,确认当前代码结构下这个 场景不可达(每条留痕产生路径都必然先经过一次会写决策快照的门控评估), 判定不成立。 ## Test plan - [x] `uv run pytest`:1688 passed - [x] `uv run ruff check .`:clean - [x] `uv run basedpyright src/nonebot_plugin_arise/entry_drive_tick.py`:0 errors - [x] 变异测试验证所有新守卫(8 处逐一变异验红后恢复) - [x] 两轴 review(sonnet)+ 对抗式复核,3 条确认发现全部处理 - [x] ADR-0021/0022 更新节已在 `docs` 分支本地提交(不推送)
ADR-0021 拍板的"判断结论带简短理由(供 /why)"与"判断失败记入 /why 标注
'判断失败'"此前一件都没落地,五处调用点(反思闭环画像/技能候选、Delta
压缩 Knowledge 候选、习得贴纸、跨平台动态拉取)判否后直接丢弃 verdict。
新增 drift_veto.py 存储port切片 + judge_and_maybe_veto 唯一入口统一接入,
/why 追加渲染这条留痕。

ADR-0022 更新节拍板的"不得编造声明来源之外的核心身份类事实"约束此前
从未写进 SECURITY_BASELINE,现补上第五条 bullet(不新增 port/存储/
子系统,纯文本追加)。
- cross_platform_pull 判否后没走 judge_and_maybe_veto,而是在
  entry_drive_tick.py 里手写了一份等价的 if verdict.status != "pass"
  判断,与 drift_veto.py/test_drift_veto.py 文档"唯一入口"的表述矛盾
  (medium,两轴 review 均独立抓到同一根因)。拆出 record_veto_if_needed
  (judge_and_maybe_veto 的后半段)给 entry_drive_tick.py 复用,"该不该
  留痕"判定只剩一份代码;同步订正两处文档措辞 + 补一条等价性回归测试。
- PullOutcome 去 Optional 化后 asyncio.gather 的类型推断失效,
  basedpyright 报 2 个新错(不挡 CI 的 ty,但影响本地 IDE 体验,low)。
  改用显式 list[asyncio.Task[Any]] 收集后再 gather,basedpyright 验证
  归零。
Yushu merged commit 777993599d into main 2026-08-12 07:48:38 +00:00
Yushu deleted branch feat/116-drift-guard-trace-and-identity-baseline 2026-08-12 07:48:39 +00:00
Sign in to join this conversation.
No description provided.