文件和行号
internal/knight/project_proposal.go 行 116-145(gitStatusSnapshot),配合行 45-80(filterKnightBookkeeping)与行 182-200(guardrail 比较)
问题描述
gitStatusSnapshot 在 monorepo 子目录(--show-prefix 非空,如 mobile/)归一化 porcelain 路径时,注释(行 126)声称 "strip the show-prefix from each path (both sides of 'R old -> new')",但实现 line[:3] + line[3+len(prefix):] 只从整行 path 开头(rename 的 src 侧)剥离 prefix,dst 侧(" -> " 之后)仍保留 mobile/ 前缀。
实测确认(真实 git 仓库子目录下):
R mobile/.ggcode/a.md -> mobile/.ggcode/b.md → 归一化后: R .ggcode/a.md -> mobile/.ggcode/b.md
后果:filterKnightBookkeeping 的 inGG() 用 HasPrefix(p, ".ggcode/") 判断,src 命中但 dst 不命中 → #1617/#1576-C 钉死的"纯 .ggcode 域内 rename 豁免"在 monorepo 子目录下失效(豁免分支要求 BOTH 侧 inGG)。
触发场景
monorepo 子目录 workspace + .ggcode/ 已提交 + guardrail 快照窗口内出现/消失 staged rename(R)状态行 → 豁免失效 → 假阳性 "READ-ONLY GUARDRAIL VIOLATED" → 提案被误丢弃(可重试恢复)。
实际触发面极窄:proposal 流程自身从不产生 R 行(append 写入是 ??/M;AtomicWriteFile 覆盖同路径;os.Rename 工作树 rename 显示为 M/?? 而非 R——R 需要 index 中的 rename)。且失败方向是 fail-closed(多报警),不产生安全绕过(跨域 rename 因 dst 带前缀反而正确地不被豁免)。
预期行为 vs 实际行为
预期:rename 两侧均剥离 show-prefix,实现与注释一致。
实际:仅 src 侧剥离,dst 侧保留前缀,子目录下 .ggcode 域内 rename 豁免失效。
修复建议
对 " -> " 之后部分同样剥离 prefix;或改用 git status --porcelain --relative(git 2.43+ 原生支持,两侧均按 cwd 相对输出),可整段删除手工归一化。
严重程度
low(实现与注释语义不符 + 豁免逻辑在特定拓扑下失效,但正常流程不可触发,fail-closed)
验证
独立复核 subagent(sa-99)实测 git 行为确认:src 侧剥离、dst 侧保留,#1617 测试所 pin 行为在子目录下失效。
文件和行号
internal/knight/project_proposal.go行 116-145(gitStatusSnapshot),配合行 45-80(filterKnightBookkeeping)与行 182-200(guardrail 比较)问题描述
gitStatusSnapshot在 monorepo 子目录(--show-prefix非空,如mobile/)归一化 porcelain 路径时,注释(行 126)声称 "strip the show-prefix from each path (both sides of 'R old -> new')",但实现line[:3] + line[3+len(prefix):]只从整行 path 开头(rename 的 src 侧)剥离 prefix,dst 侧(" -> " 之后)仍保留mobile/前缀。实测确认(真实 git 仓库子目录下):
后果:
filterKnightBookkeeping的inGG()用HasPrefix(p, ".ggcode/")判断,src 命中但 dst 不命中 → #1617/#1576-C 钉死的"纯 .ggcode 域内 rename 豁免"在 monorepo 子目录下失效(豁免分支要求 BOTH 侧 inGG)。触发场景
monorepo 子目录 workspace +
.ggcode/已提交 + guardrail 快照窗口内出现/消失 staged rename(R)状态行 → 豁免失效 → 假阳性 "READ-ONLY GUARDRAIL VIOLATED" → 提案被误丢弃(可重试恢复)。实际触发面极窄:proposal 流程自身从不产生
R行(append 写入是??/M;AtomicWriteFile 覆盖同路径;os.Rename 工作树 rename 显示为M/??而非R——R需要 index 中的 rename)。且失败方向是 fail-closed(多报警),不产生安全绕过(跨域 rename 因 dst 带前缀反而正确地不被豁免)。预期行为 vs 实际行为
预期:rename 两侧均剥离 show-prefix,实现与注释一致。
实际:仅 src 侧剥离,dst 侧保留前缀,子目录下 .ggcode 域内 rename 豁免失效。
修复建议
对 " -> " 之后部分同样剥离 prefix;或改用
git status --porcelain --relative(git 2.43+ 原生支持,两侧均按 cwd 相对输出),可整段删除手工归一化。严重程度
low(实现与注释语义不符 + 豁免逻辑在特定拓扑下失效,但正常流程不可触发,fail-closed)
验证
独立复核 subagent(sa-99)实测 git 行为确认:src 侧剥离、dst 侧保留,#1617 测试所 pin 行为在子目录下失效。