Skip to content

[code-review] gitStatusSnapshot rename dst side keeps show-prefix, breaking .ggcode rename exemption in monorepo subdirs #2755

Description

@topcheer

文件和行号

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 行为在子目录下失效。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions