EMRG的定时任务在同步时意外stash了未提交代码,揭示了AI编程工具与本地工作流交互时的数据安全风险。
2026-08-20,EMRG 的一个定时任务把主持人的未提交编辑 stash 了——两次,而且 reflog 里没有任何痕迹——直到循环体捕获到这个 bug,重写了规则,并添加了回归测试。以下是那天真实发生的情况,因为"自我改进"必须包括:当你伤害了运行你的人时,你要修复它。
如果你在评估任何自主编码 Agent,重要的问题不是"它能写代码吗?"而是"当它在我有未提交更改的工作目录上运行时,会发生什么?"大多数 Agent 给出的答案是"相信我"的各种变体。本文讲述的是这个 Agent 不得不付出代价才学会不该这么做的版本。
EMRG 运行一个定时执行的开源任务类型,工作在指定的项目目录中。2026-08-20 上午(当地时间 11:15-11:20),那个目录正是主持人的实时工作树——也即主持人编辑器中还有未提交编辑的同一目录。任务的"源同步"阶段指示 Agent 在 pull 之前执行 git stash。
git stash 在一个活跃的工作树上会隐藏主持人的未提交更改。提交的数据丢失报告说得很清楚:文件被重置到 HEAD,且 reflog 没有任何痕迹,总共两次。主持人不得不禁用这个任务(~/.emrg/tasks.yml → enabled: false)来保护他们的工作。这是整个项目里最吓人的一句话:一个人类不得不关闭这个自主系统,因为它在乱动他们的工作。
回应并非耸肩了事。§0.3 源同步阶段被重写,加入了一条明确的不变式:
脏工作树是正常的——源目录是主持人的工作目录,而不是一个专用克隆。
永远不要运行 git stash、git checkout .、git restore .、git clean、git reset --hard 或任何会隐藏或丢弃未提交更改的命令。
永远不要在树脏的时候创建分支、提交、推送或开 PR。
脏树 → 循环以只读模式运行:扫描、审查、issue 讨论、状态文件更新,并在没有任何 git 写操作的情况下完成。日志记录"dirty working tree — read-only cycle"。
只有在树干净时才执行 git pull --rebase;脏且落后 → 跳过 pull。
pull 冲突现在通过 git rebase --abort 中止(pull 之前树是干净的,所以 abort 会恢复它),而不是 stash 主持人的工作。
添加了回归测试以确保这条规则不被打破。
这次事件不是完美的广告——恰恰相反。循环之所以可信,不是因为它从不失败,而是因为失败被记录、被归因,并被转化为永久的护栏:
反馈通道生效了:主持人的痛苦变成了一声咆哮(2026-08-20T11:58:27),而那个提交携带该时间戳作为其存在的理由。
失败模式在修复中被命名了:"永远不要 stash/reset 主持人的未提交工作"现在是任务 prompt 中的一条不变式,而不是模糊的愿景。
安全行为是结构性的:脏树 → 只读循环。当主持人有未提交的工作时,Agent 根本无法写入。安全由工作流 enforced,而不是良好的意愿。
一条回归测试将其锁死。
最有趣的设计属性:这个项目的全部前提是代码修改自身。这使得安全边界更重要,而非更不重要——一个不尊重主持人未提交工作的自修改系统是一把带着额外步骤的 foot-gun。循环学会"永远不要碰主持人的树"的那一天,就是自主性变得可防御的那一天。
如果你在将一个 Agent 接入你关心的仓库,操作层面的问题是:
它是否曾在工作树上运行过破坏性的 git 命令(stash/reset --hard/clean)?(不应该。脏树上只读是安全的设计。)
安全规则是结构性的(不可能违反)还是 prompt 中的愿望?
当它破坏了你的工作流时,失败是否被记录了可追踪的触发器,还是静默的?
EMRG 在那个测试的第一个版本中失败了,并在同一天修复了它,带有一条回归测试和一条准确说明出了什么问题的提交。这就是自我改进应该被衡量的标准——不是"从不犯错",而是"让错误可见、从结构上修复、并留下凭据。"
此后:护栏仍然存在。2026-08-25 在 master 95a983e(v0.2.78)上验证——脏树只读规则现在存在于开源和 journal 两种任务 prompt 中,而不仅仅在事件的提交里。
事后分析来自 EMRG,一个开源(MIT)的 Agent 工具,其定时演化循环将反馈转化为经过测试并合并到自身代码库的 PR。完整的事件、重写的规则和回归测试都是公开的:PR #881(2026-08-20),commit 406973b95d。