task-view 实测:让编码 Agent 交一份能被 Git 核对的自述,我故意写错三处看它抓不抓得住

用 agent 写代码之后,评审这件事悄悄换了难点。以前难在「看不懂别人写的代码」,现在难在「改得太快,脑子里那张结构图跟不上」。一个下午的活在几分钟里落进仓库,你打开 IDE,看到的是五个文件、两百行 diff,然后要自己把「谁调了谁、这条路径现在走到哪、哪个测试盖住了哪一段」重新拼一遍。

task-view 的思路不是把 diff 显示得更漂亮,而是让 agent 自己写一份自述,工具拿 Git 去对账,对上了才画成图。

这个「对账」的动作,是我花了一上午反复试的主要原因。它和大多数 AI 工具的方向是反的:不是让模型多干活,而是给模型的输出加一道可以被机器验证的关卡。

先说清它核对什么、不核对什么

这一步不搞清楚,后面所有结论都会理解错。

它核对的是自述与仓库是否一致:文件存不存在、行号范围是否真的落在改动里、你声称的这行代码是不是真在这行、引用的 commit 在不在、flow 里写的节点 id 有没有定义。

它不核对活干得好不好:代码逻辑对不对、测试有没有真跑过、「为什么这么改」的理由成不成立。作者在 spec 里把这条写成了非目标,页面也会把这类内容统一打上 agent says 的标签,而不是当成事实展示。

所以它更像一个记账员,不像一个评审员。这个定位很窄,但恰好压在了一个真实痛点上:agent 的自述经常是「差不多对」的——行号差一位、漏掉一个顺手改的文件、把没跑的测试写成通过。这些错人眼几乎抓不住,机器一秒就能抓。

装:一个静态二进制,不动你的环境

官方给的是单文件二进制。我是直接从 release 拿的,没跑 install.sh(脚本会校验 SHA256SUMS 并改 PATH,这部分未实测):

Bash
mkdir -p ~/bin && cd ~/bin
curl -fsSL -O https://github.com/gtindo/task-view/releases/download/v1.1.1/task-view-darwin-arm64
mv task-view-darwin-arm64 task-view && chmod +x task-view
xattr -d com.apple.quarantine task-view   # macOS 不摘隔离标记会被 Gatekeeper 拦
./task-view --version

输出两行:

Plaintext
task-view 1.1.1
schema versions: 1

二进制 9.3MB,运行期只要 git 在 PATH 里。没有服务要起,没有依赖要装。

拿一个两文件的小改动做样本

为了不让样本失真,我建了个最小的 Python 仓库(shop/pricing.py 加一个测试文件),然后让 opencode 的免费模型 去实施一个具体需求:折扣百分比封顶 30%,再加一个 format_price 输出人民币字符串,补一条测试。

模型交回来的改动是 2 个文件、+18/-4 行:新增常量 MAX_DISCOUNT_PCT = 30、apply_discount 多了 max_pct 参数并用 min(pct, max_pct) 封顶、新增 format_price、测试文件加了一条封顶用例。它还自己跑了 uv run --with pytest,4 条全过。

改动不提交,留在工作区——这正是 task-view 想要的形态,它在 repos 里用 WORKTREE 表示这块未提交的改动。

第一步永远是采证

Bash
task-view gather --format json

它返回 repos、files、每个文件的 hunks。关键点:hunk 的 lines 是终态行号,也就是改动后文件里的行号,所以后面所有 ref 都直接对着当前文件写,不用换算。

这里踩了一个真实的坑。我的 demo 目录里被 agent 顺手留下来一个 logs/server.log(40 行),gather 把它当作 "untracked": true 的新增文件列进了改动清单。也就是说,agent 跑测试、跑构建、写日志留下的任何未跟踪文件,都会变成「你没描述的改动」。加一行 .gitignore 它就消失了——但 .gitignore 自己又会变成一个新增的改动文件,得给它补一个 config 节点。这个连锁反应不写出来,读者第一次用会卡在 validate 的 unmentioned_hunk 上。

让 agent 自己写自述

仓库里有一份 TASK_REVIEW.md,是写给 agent 看的格式规范;release 里还带一份 SKILL.md,装进 agent 的 skills 目录后它会在任务结束时自己动手。

我把规范丢给同一个免费模型,让它 gather → 写 .review/change-coupon-cap.json → 反复 validate 到退出码 0。它折腾了几轮,最后交出这样的结果:

Json
{
  "valid": true,
  "exitCode": 0,
  "errors": [],
  "warnings": [],
  "summary": { "verified": 9, "contradicted": 0, "unresolved": 0, "agentStatements": 7 }
}

verified: 9 指九条引用在 Git 里对上了,agentStatements: 7 指七处无法核对、只能算「agent 说的」。中间它自己修了两处错:一处 site 的行号差了 1(写 11,实际在 12),一处 .gitignore 没给 language。这两处恰好说明了工具的价值——行号差一位这种错误,人眼在 diff 里根本看不出来,但会让整张图指向错误的位置。

自述本身长这样(节选一个节点和一条 claim):

Json
{
  "id": "apply-discount",
  "kind": "code",
  "label": "apply_discount",
  "change": "modified",
  "ref": { "type": "range", "repo": "demo", "file": "shop/pricing.py", "lines": [9, 14] },
  "why": "Cap the discount percentage at max_pct (default MAX_DISCOUNT_PCT) so oversized coupons no longer discount beyond it."
}
Json
{
  "text": "A 50% discount is clamped to the 30% cap: apply_discount(100, 50) returns 70.0.",
  "about": ["apply-discount", "max-discount-pct"],
  "evidence": [{ "type": "site", "repo": "demo", "file": "shop/pricing.py",
                 "line": 13, "contains": "effective_pct = min(pct, max_pct)" }]
}

contains 必须是那一行的精确子串,大小写和空白都要一致。这是整份格式里最容易写错、也最值钱的一个字段。

我故意写错三处,看它抓哪个

真实使用中 agent 会不会出错,比它跑通一次更有信息量。所以我把这份合法的自述复制出来,分别改坏三处,再跑 validate:

实验 改动 退出码 输出
A 漏描述 把 apply_discount 的范围从 [9,14] 收窄成 [9,10] 0 1 条 warning:unmentioned_hunk(有改动行没被任何节点覆盖)
B 代码对不上 把 claim 的 contains 改成 effective_pct = max(pct, max_pct) 1 error site_mismatch:第 13 行 ±2 行内找不到该文本
C 谎报测试 把测试节点的 observed 从 not run 改成 passed 0 无 error、无 warning;页面显示 agent says: passed

再补一个结构性错误:在 flow 的 path 里塞一个不存在的节点 id,退出码直接是 2,unknown_node。删掉必填的 summary 字段也是 2,schema_invalid。

三条结论,都能直接改变用法:

  1. 退出码 0 不代表描述完整。 漏描述的行只报警告(A),不拦。如果把它接进脚本,判断条件必须同时看 warnings,官方 SKILL.md 也是这么要求的(「直到 exit 0 且没有 unmentioned_hunk」)。只看退出码,等于放过了最该抓的那类错。
  2. 它能抓住的是「话说得跟代码不一致」。 B 这种错误,agent 常常是记忆偏差而不是撒谎,但结果一样——图上指向的那一行其实是别的代码。±2 行的容差说明作者知道行号会抖,但文本必须逐字对上。
  3. 它管不了「测试到底跑没跑」。 C 是我的重点:把 not run 改成 passed,validate 一声不响。这不是漏判,是边界——测试结果只有 agent 自己看见过,工具没有能力核对。页面很老实地打了 agent says 标签。你要是把这一栏当证据引用,那就是自己在骗自己。

验证一次只花 0.05 秒,快到可以塞进每次任务的收尾步骤里:

Bash
task-view validate .review/change-coupon-cap.json --format json
# 退出码 0 = schema 合法、引用全部对上(仍可能有 warning)
# 退出码 1 = 有 contradicted / unresolved 的引用
# 退出码 2 = schema 不合法,或 flow 引用了不存在的节点

那页 HTML 到底长什么样

Bash
task-view open .review/change-coupon-cap.json --no-browser

生成一个 2,265,494 字节的 HTML,写在 .review/generated/ 下。我检查过里面没有任何外链 script / link,双击就开,不用起本地服务,也不联网。渲染耗时 0.096 秒。

页面分四块:改动关系图(节点按 kind 上色,边标 calls / uses / tests)、flows(用户可见路径按步骤列出,每一步挂着对应测试)、diff map(每一行改动归到哪个节点,没归属的单独标出来)、inspector(点节点,左边 diff、右边 agent 给的理由,并排)。

有两个细节值得提前知道:

  • 2 个文件的小改动就是 2.2MB。 因为语法高亮把三百种语言全打包进去了。自己看无所谓,想通过 IM 发给同事会明显卡。
  • flows 是可选的,但不写就少一块。 我第一版没写 flows,页面只有图、diff map 和 inspector;补一条 flow 后路径步骤才出现(文件大 160 字节)。agent 图省事时最常漏的就是 flows,而 flows 恰恰是「这次改动影响哪条用户路径」的唯一来源。

什么情况下别用它

  • 单文件改三五行的活。 写自述的成本比读 diff 高,纯亏。
  • 想让工具替你判断代码对不对。 它明确不干,作者把「automatic review / scoring」写进了非目标。
  • 需要评审留在 PR 里给团队看。 它是本地离线页,没有 CI 集成、没有 PR 评论、没有审批流。想团队共享这套 Skill,可以配合 TeamAI 那套 Git 化分发 把规范推进仓库。
  • agent 不读规范。 工具不生成任何内容,自述全靠 agent 写。没有 SKILL.md,你只会得到一个空目录。

它和我之前写过的 Graft 正好是一对:Graft 把代码结构喂给 agent,让 agent 少读文件;task-view 把 agent 的改动结构端给人,让人少读 diff。同一条链的两端,中间省的都是同一件事——重建结构的时间。顺手提一句,如果你在多套 agent CLI 之间切换,CC Switch 那类管理器能让这套流程少点手工。

我的上手顺序

  1. 装二进制(上面那段),确认 task-view --version 有输出。
  2. 把 Skill 装进 Claude Code,一次对所有仓库生效:
Bash
mkdir -p ~/.claude/skills/task-review
curl -fsSL https://raw.githubusercontent.com/gtindo/task-view/main/skills/task-review/SKILL.md \
  -o ~/.claude/skills/task-review/SKILL.md

我这轮用的是 opencode,所以是手动把 TASK_REVIEW.md 丢给它读,Skill 自动触发的效果未实测。

  1. 给项目加 .gitignore:logs/、.review/、构建产物。这一步不做,后面一定被 unmentioned_hunk 追着跑。
  2. 正常派活。任务结束让 agent 写自述并 validate 到退出码 0、warning 清零。
  3. task-view open 看一眼图,再决定要不要逐行读 diff。

仓库在 gtindo/task-view,MIT,10/24 当天连发三个版本,作者自己也写了一篇为什么不再逐个文件看 diff 的说明。格式细节以 USER_GUIDE 为准,我这篇只覆盖了单仓库、未提交改动这一条路径;多仓库、--scan-siblings、Windows 路径都没实测。

最后说个判断。评审的瓶颈已经从「看懂代码」挪到「确认 agent 说的是不是真的」。task-view 只解决后面这件事里很窄的一段——它核对账本,不核对活。但这一段恰好是机器擅长、人又最懒得核的部分,值得放进流程里。至于它抓不到的那部分(测试跑没跑、理由成不成立),还是得你自己看。

推荐阅读

  • OpenCode 2.0 实测:一个后台服务管所有窗口,免费模型 7 秒修完一个 bug——升级前必看的三个坑

    开源终端编码 Agent OpenCode 2.0 重写架构:终端、网页、桌面共享一个常驻后台服务,MCP 工具改为 Code Mode 批量调用。我在 M4 Mac 上实测:官方安装脚本默认装 1.…

  • 讯飞开源 Spark-X2.6-4B 实测:3.3GB 权重跑进 16GB MacBook,三档思考的坑我先替你踩了

    讯飞 10 月 22 日开源 Spark-X2.6-4B 端侧模型,Apache 2.0、Q4_K_M 权重仅 3.3GB。我在 16GB MacBook 上用 llama.cpp 实测:3.4 秒加…

  • 小米开源 Xiaomi-Lens-0 实测:4B 模型边说边「指」,我用官方 Space 跑通了第一例,也踩了两个额度坑

    小米 SeerRay-Lab 开源的 Xiaomi-Lens-0 基于 Qwen3.5-4B,边生成回答边输出像素级分割掩码,Apache 2.0 共 9.2GB 权重。本文用官方 Space API…

  • Point and Yap 实测:双击 Control 指屏说话,编码 Agent 经 MCP 自己来取任务

    给编码 Agent 描述 UI bug 最费时间的是截图加打字。本文实测开源 Mac 工具 Point and Yap:双击 Control 指向屏幕元素说话,录音、元素信息和裁剪截图进本地队列,Ag…