Direct 通道 agc_write_file 持续报「项目正在被其他写操作占用」:写入桥与 App 自身写操作争用同一把不可重入的 .agent/project.lock #318
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
1. 现象
AGC 新建项目后,在该项目的第一轮 Direct 对话里,唯一的项目写入通道
agc_write_file每次都失败:同时:
agc_list_registered_assets、agc_list_project_files、client_session_info都能返回)。agc_list_registered_assets返回pendingOperations: []。2. 实测证据(本机复现现场,非推测)
现场:项目
C:\Users\dongy\Documents\Genarrative GameAgent\gameagent-13951477(路径含空格),App 由.worktrees/agc-canvas-v3的 debug 构建启动。2.1 时间线(来自客户端自己的 turn 记录与文件落盘时间)
game/、package-lock.json)c6340875…开始(recordedAtMs=1789010957683).agent/runtime/direct-codex/turns/agc_write_file全部status=failed,每次durationMs仅 24-42msturn_end,completed:true,firstDesign=write:game/src/config.js)094d02c1…;同一秒.agent/runtime/locks/direct-codex-art.lock被 pid 57068 创建probe-4落盘成功(game/src/config.js=probe-4\n),同时 turn 结束、project-revision.json更新project-revision.json.agent\project.lock不存在;.agent目录可写,project.lock可正常CreateNew创建并删除关键点:每次失败都在 24-42ms 内返回,而仓库其它写入口用的是有界等待(
project_gates.rs:1818-1859,2000×5ms≈10s)。也就是说 Direct 写路径根本没有等,任何重叠都直接被判死。2.2 这不是残留锁,也不是 ACL 问题
事后复查(11:47):
.agent\project.lock不存在;.agent目录 ACL 只有WIN11_SUZUMIYA\dongy: FullControl(非继承),读写正常;CreateNew实测能立刻创建project.lock并删除;C:\Users\dongy\Documents不是 reparse point。所以「残留锁 / 权限被拒」都被排除;锁是在失败窗口内被真实持有的,并在 11:43:57 前后释放。
2.3 App 自身的进程拓扑(App 是多进程,锁是同一 App 内部在争)
agc_tools的 MCP 进程(27680)只是把调用转发到 App 自己的工具桥 HTTP 端点(GENARRATIVE_AGC_TOOL_BRIDGE_URL,由拉起 codex 的进程写入,见codex_app_server.rs:2151;该进程按进程树是 GUI 57068)。agc_write_file的落盘与取锁发生在 App 自己的进程里,不是外部程序。direct-codex-art.lock的持有者 pid 57068,与桥所在进程一致 ——「被其他写操作占用」里的"其他写操作"就是这个 App 自己的另一个写通道/另一轮工作。2.4 附带发现的锁文件残留
.agent\.manifest.json.lock(0 字节,11:29:17 创建)在项目里一直存在;同类 0 字节*.lock在gameagent-32983816、gameagent-b9c2b569也有。说明"锁文件生命周期"在 App 内普遍缺乏收口,用户看到"有锁文件"是合理怀疑对象。3. 代码核对结果(静态,已定位到行)
agc_write_file→ App 工具桥 →bridge_write_fileapps/ai-game-creator-shell/src-tauri/src/agent/direct_tools_mcp.rs:468、.../direct_tool_bridge.rs:2310.../direct_tool_bridge.rs:1476acquire_project_write_lock(root, "direct-codex.file.write")create_new独占创建.agent/project.lock.../project/filesystem.rs:238-245.../project/filesystem.rs:306-308.../project/filesystem.rs:290-305(需autonomous_game_build_root_run_active_at+ 锁属本进程).../tests/project_tools.rs:5614.../agent/runtime_actions/project_gates.rs:1818-1881PermissionDenied一律归类为"争用".../project/filesystem.rs:155-168.../project/filesystem.rs:133-153由此得到三条推断(未逐一在运行时证死,但与本机证据一致):
filesystem.rs:290-305的同进程豁免只覆盖 autonomous 通道,Direct 通道拿不到 → 必然报「项目正在被其他写操作占用」,且与用户是否真的开了别的程序无关。bridge_write_file没有走有界等待,24-42ms 就返回失败;同等争用在其它通道会被等待窗口吸收。commandId / pid / createdAt / ownerIsSelf,且把 ACL 拒绝、delete-pending、真实跨进程争用都压成同一句话;用户只能看到"有锁",看不到"谁在持锁",于是被合理误判为残留锁。4. 影响
.agent属客户端控制面 Agent 不应自行删改)。docs/project-memory/shared-memory/pitfalls.md的 planning v2 条目)、project.lock触发 Windows UAC(提交4127686e1)、同一句文案承载多种原因。5. 建议修复方向
本次诉求:这条链路本来就全在客户端进程内(MCP 工具 → App 工具桥 → 落盘),为什么还要用文件锁?直接用进程内 mutex/串行化就行。
Mutex/异步串行队列)串行化:可重入可判定、不会留下锁文件、不会把"自己人"报成"别人"。commandId / pid / createdAt与ownerIsSelf,让用户和维护者一眼看出是谁在持锁;docs/project-memory/shared-memory/decision-log.md要求错误里不得回传项目绝对路径,现有redact_agent_runtime_error已覆盖(用例tests/project_tools.rs:5807),改造时不要破坏脱敏。.agent/.manifest.json.lock这类 0 字节残留锁不应跨会话长期留存。6. 验收判据
agc_write_file成功,包含同一轮并行写多个文件。agc_write_file要么正常等待后成功,要么报出明确可行动的错误(带持有者身份),不再出现"没有其它程序在写却报被其他写操作占用"。7. 不做项 / 边界
.agent/project.lock的项目级串行化语义,也不放宽 AGC 的私有 ACL 边界。pendingOperations语义(与本问题无关)。8. 取证脚本(供维护者复核)
9. 待补充
.worktrees/agc-canvas-v3debug 构建).agent/project.lock的实际内容(commandId / pid / createdAt)——本次锁文件在复查前已被释放删除,未能取到direct-codex-art)或其它通道(turn 记录未直接给出持锁命令)