# 2026-05-05 Agent健身房复盘 一句话概括:**这次发现的不是已经自动修复的问题,而是 4 类以后会反复浪费时间的操作摩擦 / 工具缺口。** | 优先级 | 发现的问题 | 真实含义 | 计划怎么解决 | | --- | --- | --- | --- | | 1 | Night Gym 把自己的主流程和子分析又当成用户 session 扫了一遍 | 当前 10 个 session 里有 6 个是上一轮 Night Gym 自生成记录,候选会被 prompt、log、reflection 污染。 | 加 `self-run contamination` 诊断:按 cwd、prompt marker、SUBAGENT_DONE、out/sub 写入轨迹过滤,并输出污染率。 | | 2 | 无人值守日报有目标文件,但 session 没有最终回答或保存信号 | 每日 i+1 阅读这类定时任务可能静默失败;用户早上只会发现 Odyssey 里没有可读报告。 | 加产物验收 doctor:检查目标 Markdown 是否存在、非空、结构正确、有链接,并把失败原因写成 JSON。 | | 3 | Viva 写卡语境和旧卡更新逻辑缺少端到端回归台 | Gemini span 拆句会让 Anki Context 只剩目标词;旧卡更新又必须保证只改 note 字段、不重置复习状态。 | 固化 DOM fixture + AnkiConnect mock,做 `bench:context`,同时验证完整句抽取和 `updateNoteFields` 路径。 | | 4 | Codex CLI 升级排障停在安装来源和 registry 失败处 | 用户连续触发升级,但记录里没有升级后版本核验;PATH、npm、Homebrew、网络错误容易混在一起。 | 做 CLI upgrade doctor:判定 active executable 来源、远端版本、推荐命令、升级后核验和网络失败原因。 | ## 下一步 | 顺序 | 先做什么 | 为什么 | | --- | --- | --- | | 1 | 先做 Night Gym 自生成记录过滤器 | 它直接影响候选质量;不先过滤,后面的复盘会继续被自身产物放大。 | | 2 | 接入无人值守日报产物验收 | 这是用户每天会看的固定产物,静默失败的感知成本最高。 | | 3 | 把 Viva 回归台并入现有 npm test/build 前后 | 这次已修了 bug,但需要防止下一次 DOM 页面变化或 Anki 更新路径回退。 | | 4 | 沉淀 Codex CLI 升级 doctor 和回复模板 | 安装类请求会反复出现,标准化后能减少半截排障。 | ## 一、证据详情 ### 1.1 Night Gym 自生成记录过滤器 - 类型:`diagnostic-tool` - 风险:`low` - 摘要:该主题有效:上一轮 Night Gym 的主流程 prompt、子分析 session 和 reflection 被下一轮当作普通样本扫描,导致候选主题被自身产物放大。应在扫描入口增加自生成记录过滤和污染率诊断,先排除 cwd、prompt marker、输出路径等强特征,再进入候选生成。 - 证据: - `019df493-9d07-7b13-b183-0ff2bea009fc`(~/.codex/night-gym/runs/2026-05-04):上一轮主流程 session 的 agent_reflections 里混入了完整的 Night Gym 主任务 prompt,而不是真正的运行瓶颈;同一批输入还包含 5 个 Night Gym 子主题分析 session。 - `019df493-9d07-7b13-b183-0ff2bea009fc`(~/.codex/night-gym/runs/2026-05-04):上一轮主流程最终写入:本次扫描 20 个 session,产出 5 个候选;但当前输入再次扫描时,10 个 session 里有 6 个属于上一轮 Night Gym 自身或其子分析。 - `019df496-5f6f-7c40-8fcc-f691c7bf57a9`(~/.codex/night-gym/runs/2026-05-04):子分析 session 的 reflection 重复出现:记忆核对显示这不是孤立失败:同一条链路反复出现了工作树缺文件、roster 缺失、双 provider DNS 失败和 Odyssey 目录不可写。 - 主要改动: - 在 Night Gym 的 session 扫描入口新增 diagnostic tool:入口建议为 scripts/check_self_run_contamination.py 或现有 pipeline 的 prefilter 子命令;输入来源为待扫描 session JSONL/agent_reflections、session cwd、用户 prompt 文本、输出路径 out/sub/* 与 run root。 - 核心检查步骤:按 cwd 前缀 ~/.codex/night-gym/runs、prompt marker(Night Gym 子主题分析、候选主题、输出路径、SUBAGENT_DONE)、输出文件写入轨迹、session role/name 识别主流程和子分析;统计总样本、自生成样本、污染率、被过滤 session_id。 - 成功/失败信号:成功时候选生成前污染率降到可解释范围,过滤日志列出原因且业务 session 仍保留;失败时污染率高于阈值(如 20%)或任一候选 evidence 主要来自 Night Gym 自身 prompt/reflection。 ### 1.2 无人值守日报产物验收器 - 类型:`diagnostic-tool` - 风险:`low` - 摘要:这个主题有明确价值:片段同时出现了目标报告路径、无人值守日报任务类型,以及 session 侧缺少 assistant 输出和保存完成信号的问题。风险点不是内容质量偏好,而是 runner 可能静默失败,导致 Odyssey 目标文件未生成、为空、结构不对或没有链接却无人发现。 - 证据: - `019df5a6-4309-7a71-9e45-b3a7a4bef1ed`(~):用户 prompt 明确给出 Report target:~/Library/Mobile Documents/com~apple~CloudDocs/odyssey/0 收集箱/每日英语i+1阅读/2026-05-05 每日英语i+1阅读.md,并要求生成 Daily i+1 Reading Recommendations。 - `019df5a6-4309-7a71-9e45-b3a7a4bef1ed`(~):该 session 的 assistant_messages 为空,event_messages 也只有原始 runtime prompt,没有看到 Context used、Recommendations 或保存完成信号。 - `019df5a6-4309-7a71-9e45-b3a7a4bef1ed`(~):这是无人值守日报类任务:外层 runner 期望保存最终消息到 Odyssey 报告,但 session 记录里无法证明目标文件已生成、非空、结构正确或包含链接。 - 主要改动: - 新增工具入口 `tools/scheduled_report_output_doctor.py`:CLI 形态为 `python tools/scheduled_report_output_doctor.py --session-log <jsonl-or-log> --report-target <path> --expect-title "Daily i+1 Reading Recommendations" --min-links 1 --out out/diagnostics/<session_id>.json`,专门用于无人值守日报产物验收。 - 输入来源固定为三类:runner/runtime prompt 中解析出的 `Report target`,session 记录中的 `assistant_messages` 与 `event_messages`,以及目标 Markdown 文件本身。核心检查步骤包括:路径是否存在、文件大小是否超过最小阈值、是否包含期望标题或推荐段落、是否有 Markdown 链接、是否出现保存完成信号、assistant 输出是否为空。 - 成功/失败信号要机器可读:成功输出 `{status: "pass", report_target, bytes, link_count, checks: [...]}`;失败输出 `{status: "fail", failed_checks: [...], remediation: [...]}`,其中失败项至少区分 `missing_file`、`empty_file`、`no_assistant_message`、`missing_required_section`、`no_links`、`no_save_signal`。 ### 1.3 Viva 语境写卡回归台 - 类型:`tool-upgrade` - 风险:`medium` - 摘要:这个主题有效,问题不是单次数据脏,而是 Gemini 等页面把句子拆成多个 span/text node 后,Viva content.js 的语境抽取退化为只取当前 text node,导致 Anki 卡片 Context 只有目标词。后续又引入旧卡语境更新能力,且要求不改变复习状态,因此需要一个覆盖 DOM 语境抽取和 Anki 更新语义的回归台。 - 证据: - `019df8d3-237d-76e1-b2e3-64b6e820ab4a`(~/Documents/product-bu):用户问:为什么我的有的单词添加进去以后,没有这个语境的句子呢?agent 只读查 Anki collection 后发现 rack note 的 Context 字段只有 <mark>rack</mark>。 - `019df8d3-237d-76e1-b2e3-64b6e820ab4a`(~/Documents/product-bu):根因定位为 Gemini 页面把一句话拆成很多 span/text node,content.js 只从当前 text node 抽句子,可能只拿到目标词;修复需要向父级文本块追溯完整句子。 - `019df8d3-237d-76e1-b2e3-64b6e820ab4a`(~/Documents/product-bu):用户继续要求:加一个更新按钮,把旧卡片语境改成最新语境,但原来的复习状态或阶段都不要改变。agent 新增 updateNoteFields 路径并测试不能 delete/add note。 - 主要改动: - 在 Viva 浏览器插件仓库新增 `tests/fixtures/gemini-fragmented-spans.html`:构造一句话被拆成多个 `span` / text node 的 Gemini 样例,目标词位于单独 text node,断言抽出的 Context 必须包含完整句子而不是只有 `<mark>rack</mark>`。 - 把 `content.js` 中负责取当前选词语境的逻辑抽成可测试函数,例如 `src/content/contextExtractor.ts`;核心规则是从命中的 text node 向上找到最近的稳定文本块祖先,读取并规范化块级 `textContent`,再对目标词加 `<mark>`,保留当前 text node 作为 fallback。 - 新增 `tests/anki/update-context.spec.ts` 或同等测试:用 AnkiConnect mock 断言“更新旧卡语境”只调用 `updateNoteFields`,不调用 `deleteNotes` / `addNote`,并保留原 note id、复习队列、due、interval、ease 等调度字段。 ### 1.4 Codex CLI 升级来源诊断 - 类型:`diagnostic-tool` - 风险:`low` - 摘要:该主题有明确价值:用户连续两次要求升级 Codex CLI,但执行链路停在安装来源判断和 registry 查询失败处,没有形成可复用的诊断闭环。适合沉淀为一个升级 doctor,先判定当前 codex 可执行文件来自 npm、Homebrew、shim 还是其他路径,再给出升级命令、网络失败原因和升级后核验信号。 - 证据: - `019df8ce-648c-73b0-a969-d23dc0233dd8`(~/Documents/info-bu):用户先发:把cdoex的cli更新一下。该 session 的 assistant_messages 为空,只留下用户请求,可能是启动后未完成或中断。 - `019df8cf-dd89-7a12-b2ce-841c9fc96b3a`(~/Documents/info-bu):用户再次发:吧Codex cli更新一下。agent 确认当前 shell 走 /opt/homebrew/bin/codex,版本是 codex-cli 0.125.0,并继续查 npm 全局包还是 Homebrew shim。 - `019df8cf-dd89-7a12-b2ce-841c9fc96b3a`(~/Documents/info-bu):@openai/codex 是 npm 全局安装的,不是 Homebrew formula;npm registry 查询没有返回,像是网络受限;记录里没有升级后的版本核验或完成消息。 - 主要改动: - 新增工具入口 `tools/codex_cli_upgrade_doctor.py --json`,输入来源包括 `command -v -a codex`、`codex --version`、`readlink`/`realpath`、`npm root -g`、`npm list -g --depth=0 @openai/codex`、`npm view @openai/codex version`、`brew list --formula --cask | rg codex`、`brew info --formula --cask codex openai-codex`。输出当前可执行路径、安装渠道、当前版本、可见最新版本和推荐升级命令。 - 新增 skill `skills/codex-cli-upgrade-doctor/SKILL.md`:当用户说“更新/升级 Codex CLI”时,先运行 doctor。核心检查步骤为:解析 PATH 中所有 `codex` -> 判定 active executable 是否来自 npm global/bin、Homebrew shim、npx/corepack 或手工拷贝 -> 查询远端版本并记录 DNS/registry/proxy 错误 -> 根据来源选择 `npm install -g @openai/codex@latest` 或对应 brew 命令 -> 升级后重新运行 `command -v codex && codex --version`。 - 将成功/失败信号写进 `out/diagnostics/codex-cli-upgrade-YYYYMMDD-HHMMSS.json`:成功信号是 active path 未漂移、版本号变化或确认已是最新、安装包来源与推荐命令一致;失败信号是 npm registry 无响应、Homebrew 无 formula/cask、升级后 `codex --version` 仍旧、PATH 中存在多个冲突 codex、或升级命令成功但 shell 命中的仍是旧 shim。 ## 二、候选文件 | 候选 id | 示例文件 | 审批状态 | | --- | --- | --- | | `night-gym-self-run-filter` | `out/examples/night-gym-self-run-filter.md` | `pending` | | `scheduled-report-output-doctor` | `out/examples/scheduled-report-output-doctor.md` | `pending` | | `viva-anki-context-regression-bench` | `out/examples/viva-anki-context-regression-bench.md` | `pending` | | `codex-cli-upgrade-doctor` | `out/examples/codex-cli-upgrade-doctor.md` | `pending` |