# TODO - Reader MCP 正式化 > 本文件用于架构与 Codex 协作同步。 > > 规则: > - `TODO` = 未开始 > - `DOING` = 正在进行 > - `DONE` = 已完成 > - 每次只允许一个最高优先级主任务处于 `DOING` ## 0. 协作约束 开始编码前必须阅读: 1. `README.md` 2. `docs/README.md` 3. `docs/openclaw/README.md` 4. `docs/openclaw/openclaw-handoff.md` 5. `docs/openclaw/openclaw-orchestration-flow.md` 6. `plans/README.md` 7. `plans/reader-mcp-architecture-design.md` 8. `plans/reader-mcp-implementation-plan.md` 9. 本文件 10. `plans/issues/2026-04-06-reader-digest-sigterm.md` --- ## 1. 当前主任务 ### [DONE][P0] 建立 run-state 运行态基础设施 目标: - 给 freshrss pipeline 引入正式 run state - 即使失败或中断,也能留下明确运行真相 要求: - 新增 `RunState / StageState / ArtifactRecord` 模型 - 在 `outputs/freshrss/rerun//run-state.json` 持久化 - 至少覆盖以下 stages: - `fetch_feed` - `extract_articles` - `generate_summaries` - `apply_filters` - `build_delivery_payload` - `write_run_report` - 失败时写入失败阶段与错误摘要 - 不破坏现有输出目录兼容性 建议文件: - `src/summary_mcp/runtime/state_models.py` - `src/summary_mcp/runtime/run_store.py` - `src/summary_mcp/workflows/...` 完成标准: - 跑一次 pipeline 后,无论成功失败,都存在 `run-state.json` - 文件中可看出当前/最后阶段、整体状态、关键 artifacts 进展备注: - 2026-04-07:架构设计文档已建立;开始进入实现阶段。 - 2026-04-07:已新增 `runtime` 包骨架,落地 `RunState / StageState / ArtifactRecord` 与文件存储接口。 - 2026-04-07:已将 `run-state.json` 接入 `freshrss` 主流程,按阶段持续写入状态与关键 artifacts。 - 2026-04-07:已完成成功/失败路径自检,确认 `run-state.json` 在两类路径下都保留且不改变既有对外返回字段。 --- ## 2. 后续任务队列 ### [DONE][P1] 增加 MCP 状态查询接口 `get_run_status` 目标: - 可通过 MCP 查询 run 状态 要求: - 输入 `run_id` - 返回 status / current_stage / completed_stages / failed_stage / artifacts / recovery 完成情况: - 已通过 MCP 暴露 `get_run_status` - 优先读取 `run-state.json`;对无 `run-state.json` 的历史 run 兼容基于现有 run 目录与 `run-report.json` 推断状态 - 返回补充了 `progress` / `output_dir` / `state_source`,便于 OpenClaw 稳定消费且不必手拼路径 改动文件: - `src/summary_mcp/runtime/query_service.py` - `src/summary_mcp/runtime/run_store.py` - `src/summary_mcp/runtime/__init__.py` - `src/summary_mcp/server.py` 遗留风险: - 历史 run 若缺少 `run-state.json`,其阶段状态只能基于现有目录与 `run-report.json` 做保守推断 --- ### [DONE][P1] 增加 MCP 查询接口 `list_runs` 目标: - 查看近期 runs 要求: - 支持按 workflow / status / latest_n 过滤 完成情况: - 已通过 MCP 暴露 `list_runs` - 支持按 `workflow` / `status` / `latest_n` 过滤近期 runs - 返回 `run_id`、`status`、`progress`、`recovery`、`output_dir` 与 `state_source` 改动文件: - `src/summary_mcp/runtime/query_service.py` - `src/summary_mcp/runtime/__init__.py` - `src/summary_mcp/server.py` 遗留风险: - 当前按文件系统扫描 `outputs/freshrss/rerun/` 聚合,规模继续增大时可能需要再评估缓存或索引,但本阶段先保持文件系统真相 --- ### [DONE][P1] 增加 MCP 查询接口 `list_run_artifacts` 目标: - 统一列出 run 下 artifact 完成情况: - 已通过 MCP 暴露 `list_run_artifacts` - 对新 run 优先返回 `run-state.json` 已注册 artifacts,并补充 run 目录扫描发现的标准产物 - 对历史 run 直接基于现有 run 目录发现标准产物,保持兼容 改动文件: - `src/summary_mcp/runtime/query_service.py` - `src/summary_mcp/runtime/__init__.py` - `src/summary_mcp/server.py` 遗留风险: - 当前仅补充扫描固定的一组标准产物;未注册且不在标准集合内的调试文件不会进入稳定 artifact 列表 --- ### [DONE][P1] 增加 MCP 结果读取接口 `get_delivery_payload` 目标: - 按 run_id 读取 delivery payload 完成情况: - 已通过 MCP 暴露 `get_delivery_payload` - 查询优先复用 `run-state.json` 已注册 artifacts,其次回退标准产物路径、`run-report.json` 引用和 run 目录扫描 - 返回补充了 `artifact`、`payload_schema_version`、`generated_at`、`delivery_date`、`candidate_count`、`stats`,并保留完整 `payload` 改动文件: - `src/summary_mcp/runtime/query_service.py` - `src/summary_mcp/server.py` - `src/summary_mcp/runtime/__init__.py` 遗留风险: - 历史 run 的 payload 若既未注册也不在标准路径下,只能依赖 `run-report.json` 引用或目录扫描做兼容发现 --- ### [DONE][P1] 增加 MCP 结果读取接口 `get_run_report` 目标: - 按 run_id 读取 run report 完成情况: - 已通过 MCP 暴露 `get_run_report` - 查询优先复用 `run-state.json` 已注册 artifacts,其次回退标准产物路径、历史 `run-report.json` 固定位置和 run 目录扫描 - 返回补充了 `artifact`、核心计数摘要、规范化后的关键产物路径与 `keyword_index`,并保留完整 `report` 改动文件: - `src/summary_mcp/runtime/query_service.py` - `src/summary_mcp/runtime/__init__.py` - `src/summary_mcp/server.py` 遗留风险: - 历史 run 的 `run-report.json` 内嵌路径仍保留原始绝对路径于 `report` 字段,当前仅在顶层摘要字段做规范化,避免改变历史文件真相 --- ### [DONE][P2] 设计并实现 `resume_run` 目标: - 基于 `run-state.json` 和现有中间产物继续执行 说明: - 先做最小可用恢复 - 暂不追求任意 stage 任意重入 - 设计约束已补充到 `plans/resume-run-minimal-design.md` - 第一版只支持 freshrss workflow 且仅支持有 `run-state.json` 的 run - 第一版仅考虑从最近可恢复点继续;`fetch_feed` / `extract_articles` 暂不支持恢复 进展备注: - 2026-04-07:已完成 `resume_run` minimal design 与现有 runtime/workflow/server 代码对齐分析,开始实现最小恢复链路。 - 2026-04-07:已完成 `resume_run` 最小实现编码,新增 runtime 恢复服务并接入 MCP server;当前进入设计对齐与本地自检。 - 2026-04-07:已完成 `resume_run` 架构对齐与本地自检;已验证 `write_run_report` 可恢复,且 `extract_articles` 会被明确拒绝恢复。 - 2026-04-14:已补 `inspect_resume_plan`、artifact-first 恢复判定,以及生产模式下稳定 `summary-batch` / `candidate-batch` artifacts;当前 `resume` 的剩余主问题不再是恢复点判断,而是同步执行模型仍可能让 OpenClaw 恢复阶段超时。 --- ### [DONE][P1] 把 `resume_run` 升级为最小真异步 job 目标: - 解决 `resume_run` 在 OpenClaw → MCP 同步链路里仍可能超时的问题 - 让恢复也具备“启动 / 轮询 / 读取结果”的正式控制面 要求: - 新增最小异步接口: - `start_resume_job` - `get_resume_job_status` - `get_resume_job_result` - 状态目录固定落到: - `outputs/freshrss/resume_jobs//` - 至少包含: - `run-state.json` - `input.json` - `result.json`(成功时) - `job-report.json` - 启动前必须先走 `inspect_resume_plan` - 业务执行继续复用现有 `resume_service`,不要重写恢复主逻辑 - `resume_run` 保留为同步 debug / fallback 路径,但不再作为 OpenClaw 的默认恢复入口 完成标准: - 可恢复 run 上,`start_resume_job` 能成功返回 `job_id` - `get_resume_job_status` 能稳定反映恢复 job 生命周期 - `get_resume_job_result` 能稳定返回 `run_id`、`resume_from_stage`、最终状态与关键产物路径 - 恢复耗时超过单次 MCP 同步窗口时,OpenClaw 仍不会因为同步调用挂住 进展备注: - 2026-04-14:已落地 `src/summary_mcp/runtime/resume_jobs.py` 与 `scripts/run_resume_job.py`,新增 `start_resume_job` / `get_resume_job_status` / `get_resume_job_result` - 2026-04-14:启动前会先走 `inspect_resume_plan`;不可恢复 run 会在 job 输入校验阶段直接失败,不进入后台恢复执行 - 2026-04-14:后台执行复用现有 `_resume_freshrss_run(...)`,没有重写恢复主逻辑 - 2026-04-14:已完成本地 synthetic 验证:`write_run_report` 恢复可通过 `start -> poll -> result` 闭环成功收敛 --- ### [DONE][P1] 单篇总结改为最小真异步 job 目标: - 解决 `generate_article_summaries` 在 OpenClaw → MCP 同步链路里易 timeout 的问题 - 将单篇总结正式升级为可启动、可轮询、可读取结果的异步 job 要求: - 新增最小异步接口: - `start_article_summary_job` - `get_article_summary_job_status` - `get_article_summary_job_result` - 状态目录固定落到: - `outputs/freshrss/article_summary_jobs//` - 至少包含: - `run-state.json` - `input.json` - `result.json`(成功时) - `job-report.json` - 执行模型优先使用后台子进程,不使用线程 - 业务逻辑继续复用 `summarize_selected_articles(...)`,不要重写正文总结核心逻辑 - 对 OpenClaw / reader-digest-flow 而言,异步 job 成功后应可继续接 IMA 沉淀闭环 当前进展: - 2026-04-10:已完成方案文档 `plans/article-summary-async-job-plan.md` - 2026-04-10:已落地最小代码骨架: - `src/summary_mcp/runtime/article_summary_jobs.py` - `scripts/run_article_summary_job.py` - `src/summary_mcp/server.py` 已新增 3 个 async job tools - 2026-04-10:已用真实 extracted 文件验证最小异步链路可跑通,job 能成功进入 `running → success`,并可读回结果 - 2026-04-10:已补 README / OpenClaw handoff 文档,并对外统一为 `start_article_summary_job` / `get_article_summary_job_status` / `get_article_summary_job_result` - 2026-04-10:已完成聚焦自检: - MCP tool 注册名校验通过 - stubbed async job 成功路径通过 - stubbed async job 失败路径通过,`error_summary` 与 `job-report.json` 可回读 下一步: - 将 `reader-digest-flow` 正式默认路径切到 async job - 用真实 LLM 配置再做一次非 stub 的服务端冒烟验证 --- ### [DOING][P0] FreshRSS 主日报 run 改为最小真异步 job 目标: - 解决 `run_freshrss_openclaw_pipeline` 在正式生产链路里仍为同步 MCP 调用、易超时的问题 - 将 FreshRSS 主日报启动路径升级为可启动、可轮询、可读取结果的异步 job 要求: - 新增最小异步接口: - `start_freshrss_pipeline_job` - `get_freshrss_pipeline_job_status` - `get_freshrss_pipeline_job_result` - 状态目录固定落到: - `outputs/freshrss/pipeline_jobs//` - 至少包含: - `run-state.json` - `input.json` - `result.json`(成功时) - `job-report.json` - 执行模型优先使用后台子进程,不使用线程 - 主业务逻辑继续复用 `run_freshrss_pipeline(...)`,不要重写日报核心逻辑 - job 成功后结果中必须带回 `run_id` 与关键产物路径 - README / handoff / OpenClaw 生产建议路径需要同步改成 async start path 当前进展: - 2026-04-11:问题定位完成,确认之前异步化的是 article-summary,不是主日报 run - 2026-04-11:已新增方案文档 `plans/freshrss-pipeline-async-job-plan.md` 下一步: - 复用 article-summary job runtime 骨架实现主日报 async job - 新增后台 runner 脚本 - 暴露 3 个 MCP tools - 用真实 MCP 冒烟验证 `start -> status -> result` --- ### [TODO][P2] 评估 `rerun_stage` 是否值得进入第一阶段 目标: - 在 `resume_run` 之后评估是否继续增加更细粒度补跑能力 --- ### [TODO][P2] 调整 `run_freshrss_openclaw_pipeline` 内部实现以复用 runtime 目标: - 保持外部兼容 - 内部不再是黑箱长函数 --- ### [DONE][P3] 更新 README / handoff / docs,明确 MCP 为正式入口 目标: - 把生产建议从 CLI 迁移到 MCP - CLI 明确降级为 debug / fallback 完成情况: - 已更新 `README.md`,补齐 reader 作为正式 MCP workflow service 的当前能力边界、推荐调用路径、已支持 tools 与最小 `resume_run` 范围 - 已更新 `docs/openclaw/openclaw-handoff.md`,明确 OpenClaw 应优先通过 MCP 读取 run 状态与结果,不再自己拼接 reader 输出路径 改动文件: - `README.md` - `docs/openclaw/openclaw-handoff.md` 遗留风险: - 当前仍无独立 `get_digest_brief` tool;若下游确实需要该产物,仍应先通过 `list_run_artifacts` / `get_run_report` 发现,而不是写死路径 --- ## 3. 记录区 ### 已完成记录 - 2026-04-07:新增架构设计文档 `plans/reader-mcp-architecture-design.md` - 2026-04-07:新增实施计划文档 `plans/reader-mcp-implementation-plan.md` - 2026-04-07:完成 `freshrss` pipeline 的 run-state 基础设施,新增 `runtime` 包并覆盖关键 stages 状态持久化。 - 2026-04-14:已补 OpenClaw 文档导航、历史归档、design/notes/plans 导航,并统一当前正式口径为 async job 编排入口。 ### 风险提醒 - 不要在第一阶段引入复杂任务队列 - 不要让 CLI 和 MCP 背后变成两套独立逻辑 - 若实现偏离架构,先更新 `plans/` 再改代码