Skip to content

Coverage reports all-stale with staleReason=null for every selector #84

Description

@catoncat

Update: 修正诊断 (2026-06-26)

原 issue 说"staleReason=null"是字段不存在导致的误判——CoverageInventoryStatus(数组条目)只有 freshness: "fresh" | "stale",没有 staleReason 字段。但"全部 stale"这个现象本身需要深究。

诊断脚本结果(53 条 coverage,2 fresh / 51 stale):

id=72 kind=all  fpMatch=false fspMatch=true  countMatch=true   → content changed (advisory)
id=80 kind=cwd  fpMatch=false fspMatch=false countMatch=false  → file set grew 508→528 (real stale)
id=113 kind=cwd fpMatch=false fspMatch=true  countMatch=false  → file set grew 43→45 (real stale)
id=112 kind=cwd fpMatch=false fspMatch=true  countMatch=false  → file set grew 8→9 (real stale)
id=81 kind=cwd  fpMatch=false fspMatch=true  countMatch=false  → file set grew 38→50 (real stale)

真正的 bug

"全部 stale"本身是正确行为——codex session 不断新增,coverage 当然 stale。但有两个缺陷:

  1. CoverageInventoryStatus 缺少 staleReason 字段:用户/agent 无法区分 "advisory (content changed, 查询仍可用)" 和 "real stale (file set grew, 需要 sync)"。RequestedCoverageStatusstaleReason,但数组条目没有。

  2. isAdvisorySourceContentStale 只作用于 requestedCoverage:coverage 数组里的条目无法享受 advisory 判定,全部裸露为 "stale",造成"全部 stale"的恐慌性 UX。

Fix

  1. CoverageInventoryStatusstaleReason: "none" | "source_content_changed" | "source_set_changed" 字段。
  2. toCoverageInventoryStatus 里计算 staleReason,逻辑复用 requestedCoverageStaleReason
  3. (可选)给 CoverageInventoryStatusadvisory: boolean 字段,对 codex + source_content_changed 标 true。

Impact

  • 6 个 agent 在真实数据测试中 3/6 报告 coverage 警告噪音。有了 staleReason 后,agent 可以判断"advisory 的忽略,real stale 的才 sync"。
  • 论文 RQ3 (dynamic update robustness):区分 advisory 和 real stale 是 maintenance 模块的基本要求。

原始 issue (保留供参考)

shlog status --source codex --json 显示几乎所有 coverage 都是 freshness=stale。用户/agent 无法判断是否需要 sync。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions