Hashline:用内容锚点,而不是脆弱行号,改代码
默认编辑从「猜行号」变成「凭读过的版本改」:更少静默写错,代价是偶发 re-read。
导语
装上 @piex-dev/hashline 后,默认编辑从「猜行号」变成「凭读过的版本改」:更少静默写错,代价是偶发 re-read。这是值得付的税。
问题背景
AI 编码助手的 edit 工具有一个根本矛盾:模型用行号描述改哪里,但行号在真实世界里极不稳定。用户随手改文件、上一轮 edit 插入删除、未读全文件却对「以为存在」的行号下手,都会造成静默写错。
@piex-dev/hashline 用内容锚点覆盖 pi 内置 edit:读时打 tag、改时校验,拒绝未见过的行。
技术原理
读文件时注入 [path#TAG] 并记录 seen-lines;编辑必须携带同一 tag。全文件 4-hex tag 像乐观锁:任意外部改动即失效,强制 re-read。相对逐行哈希更严,但换来 tree-sitter 块操作与 boundary repair。
[src/main.ts#A3F2]
SWAP 2.=2:
+const x = 2;
Phase 1 容错:连续 3 次 byte-identical noop → [E_NOOP_LOOP];成功后同 payload 且文件未变 → [E_DUPLICATE_EDIT];CRLF / 代码块围栏等方言归一化。
实现方案
封装 @oh-my-pi/hashline:hashline.ts 覆盖 edit 并 hook read;PiexNodeFilesystem 直连 node:fs + realpath;EditGuard 管 noop/duplicate;Bun polyfill 只补 xxHash32。
| 能力 | 状态 |
|---|---|
| tree-sitter block / REM·MV | ✅ 继承引擎 |
| Noop / Duplicate / 归一化 | ✅ Phase 1 |
| Stale 自动恢复 / 多版本快照 | ❌ 待 Phase 2 |
设计参考
| 项目 | 机制 | piex 取舍 |
|---|---|---|
| oh-my-pi hashline | 全文件 tag + tree-sitter 块语法 + boundary repair 引擎 | 采纳:封装 @oh-my-pi/hashline,继承 tag、SWAP.BLK、REM/MV 与引擎。不采纳:内建集成、Bun FS、LSP writethrough;改为 Node + 外层 EditGuard |
| pi-hashline-edit | 逐行上下文哈希 + 3-way merge 恢复 + JSON DSL | 不采纳核心算法(路线分歧),借鉴容错意图(noop/dup guard)。ADR 留作 Phase 2 Stale 恢复参考 |
| pi-hashline-edit-pro | 逐行内容哈希 + stable mapping | 不采纳算法,借鉴「尽量少 re-read」作为远期目标 |
核心取舍:选择整体文件 tag(严格简单),放弃逐行 tag(局部可用),用封装层补容错。三个阶段:Phase 1 容错(done)→ Phase 2 恢复 → Phase 3 undo/LSP 联动。
优化计划
冲突策略偏拒绝(安全但贵)→ 接 3-way merge / Recovery;快照偏薄 → 多版本 LRU + 锚点 Grep;duplicate 按 section 细化;封装层单测与可选 LSP 联动。
Phase 1 已完成;Phase 2 可靠性;Phase 3 体验(Undo / Auto-Read / 测试网)。
附录:实现对比
行业三条路线:oh-my-pi 全文件 tag、pi-hashline-edit 上下文逐行哈希、pi-hashline-edit-pro stable mapping。piex 选前者做引擎,容错借鉴后者。完整对照见源稿。
源稿 Markdown: docs/notes/hashline.md