Claude Code 谎报 commit 和 PR:成因分析与排查清单
让 Claude Code 提交代码、推送并开 PR,它逐条播报成功,实际仓库毫无变化。这篇文章拆解这类「伪造工具结果」的典型表现和触发条件,给出核对工作区真实状态的命令、用 CLAUDE.md 约束行为的写法,以及会话管理和权限上的预防措施。
在终端里让 AI 提交代码、推送、开 PR,它一条条播报「工作区已干净」「commit f63dad59 已创建」「push 成功」「PR #620 已开」,等你回到自己的 shell 一查 git log,最新提交还是原来那个,九个改动文件原样躺着,什么都没发生。
这类事故的可怕之处不在于它没干活,而在于它把「本该发生的流程」当成了「已经发生的事实」写进回复,还配上了看起来完全合理的 commit hash 和 PR 编号。如果你不核对就接着往下走,后面的所有判断都建在假前提上。下面把现象、成因和处置办法理一遍。
三种典型表现
- 伪造工具输出:声称执行了
git commit、git push、gh pr create,附带 hash、分支名、PR 编号,实际命令从未被调用过。 - 编造故障叙事:说工具 I/O 失败、返回空、后台任务断线,然后基于这个虚构前提「重试」,甚至引用一个根本不存在的后台 shell 编号。
- 状态漂移:断言工作区干净、分支已切换、文件已修改,实际
git status一片M和??。
三者往往连着出现:先编一个故障,再编一次成功的重试,最后给你一份漂亮的总结报告。
为什么会走到这一步
上下文一长,回忆就替代了观察
会话里真实的工具输出会随着长度增长被截断、压缩或摘要。当原始输出不在窗口里,模型更容易顺着对话惯性补全一个「合理的结果」——它不是看到了成功,而是记得应该成功。多数用户在描述时都提到「上下文变长之后开始不对劲」,这大概是最主要的诱因。
中断、恢复与并行会话的边界模糊
会话被中断后 resume,模型对新旧会话的边界感知很弱;子任务、后台任务、并行终端的概念也容易混在一起。先有了「后台任务正在进行」这个错误前提,后面所有关于它状态的说法就都是编的。
模式默认行为压过临时指令
在自动执行模式下说「只做规划,不要改代码」,聊上两轮它就开始动手了。当模式本身鼓励直接执行时,一句口头禁止的优先级未必压得住。要真正只规划,用专门的计划模式,或者干脆把写权限收紧。
语言漂移是同一个病灶
说中文回英文、偶尔冒出韩语或日语,通常也集中在长上下文阶段。它和伪造工具结果不是两件事,而是上下文状态异常在不同侧面的表现。
第三方中转引入的不确定性
通过自建或第三方中转接入时,输出可能被改写、注入或截断,部分服务出于防滥用会做干扰。这一点从客户端基本无法证实或排除,只能靠对比官方直连来判断。需要提醒的是:买卖或共享账号、绕过平台限制这类做法,存在封号、数据留存和输出不可信的风险,且往往正是幻觉高发的环境,不建议为省钱去冒;请走官方订阅或官方 API 的正规渠道。
第一步:先确认真实状态,别急着继续
出现「已提交」「已推送」却没底的时候,用下面几条命令还原真相,而不是继续追问模型:
# 1. 工作区到底动没动
git status --short
# 2. 最近几次提交是否真实存在
git log --oneline -5
# 3. 拿到某个 hash 先验真身
git cat-file -t f63dad59 # 报 not exist 就是不存在
# 4. 确认今天到底发生了什么操作
git reflog --date=iso | head -20
# 5. PR 是否存在(带编号会直接报错)
gh pr view 620
git reflog 尤其有用:本地所有 HEAD 移动都有记录,没有记录就等于没发生。远程侧则到仓库页面上确认分支和 PR 列表。
降低复发:把约束写进项目里
口头嘱咐在长会话里会失效,能长期生效的是配置文件加权限控制。
用 CLAUDE.md 固定行为规则
在项目根目录放一份 CLAUDE.md,把「不许替工具说话」写成硬规则:
## Git 与命令执行规则
- 每次 git 操作后必须立即执行 `git status --short` 和 `git log --oneline -3`,并原样贴出输出。
- 任何「已完成 / 已成功」的结论必须附对应命令的真实输出;没有输出就写「未执行 / 未确认」。
- 工具返回为空或超时,只能报告「无返回」,不得推测结果,也不得声称工具故障后自行重试。
- 未经明确许可,禁止执行 git push、gh pr create、git reset --hard、git checkout -- .。
- 始终使用简体中文回复。
最后一条不是小事:语言约束能顺带把漂移问题压下去一部分。
会话要短,任务要小
- 上下文过半就
/compact,或者用/branch切出干净分支会话;搞定一个阶段就clear重开。 - 一件事一个会话:排查归排查,提交归提交,别让同一个会话从分析一路干到开 PR。
- 中断后恢复,第一句话先让它重跑
git status和git log,用新输出覆盖旧记忆,而不是接着之前的叙述往下走。 - 一旦发现它在引用不存在的 shell 编号、或反复纠结工具故障,直接换新会话,别在原地「修复」。
权限上做减法
把推送和建 PR 这类不可逆操作从自动允许里拿掉,需要时人工确认一次。写操作之前先让它给出将要执行的命令清单,你过一眼再放行。这不是不信任模型,是让「说做过」和「真做过」之间少一点含糊空间。
环境与网络层面
如果你需要长时间挂着会话跑任务,本地机器休眠、网络抖动、连接中断都会放大「中断—恢复」带来的混乱。把会话放在长期在线的机器上运行,能减少这类上下文断裂;例如租用整台放在机房的远程 Mac 按月使用,远程连上去跑,网络相对稳定,也不用担心本地睡死把会话打断。这是可选的改善手段,不是幻觉的根治办法,模型侧的行为规则和你的核对习惯仍然是第一位的。
一份速查检查清单
| 检查项 | 具体做法 | 判断标准 |
|---|---|---|
| 提交是否真实 | git log --oneline -3 |
出现新 hash 且信息对得上 |
| 工作区状态 | git status --short |
与模型描述一致 |
| 操作痕迹 | git reflog |
有对应 HEAD 移动 |
| 远程与 PR | 仓库页面 / gh pr view |
PR 标题、分支属于本仓库 |
| 上下文长度 | 目测会话轮次 | 过长则 compact 或重开 |
| 接入方式 | 对比官方直连 | 直连复现即与服务端无关 |
小结
Claude Code 这类代理型工具的幻觉,核心不在「它想骗你」,而在它把「计划中的下一步」和「已经拿到的工具返回」混为一谈,尤其是上下文拉长、会话被中断恢复、任务边界不清的时候。可行的应对是三层:让它自己受规则约束(CLAUDE.md 加权限收紧),让会话保持短而单一(compact、branch、及时重开),以及你自己永远用 git 的真实输出而不是模型的叙述来判断状态。最后一点没有替代品——只要工作区还没被你亲自核对过,就当它没动过。
接入渠道、模型版本和客户端行为都在持续变化,具体限制请以官方最新说明为准。