## 结论:浅修复不可行,这条需要改 harness 契约 原设想:`sync plan` 在 `brief(r, n)` 抛 `BRIEF_NOT_FOUND` 时,退到 `git show origin/main:shadow-docs/changes/<name>/brief.md` 取 blob 解析。 实测该方案卡在两处(都试过并回退): 1. **抛错点不在 sync 域内。** `cli.mjs:61` 的 `planDomain` 在进入域逻辑之前就 `readBrief(mod, r, o)`,缺省实现是硬 `brief(r, name(o))`;`cli.mjs:44` 的 `executeDomain` 同样。域侧改 `planData` 无效——它根本没被调用。 2. **取回 blob 也过不了持久化。** `cli.mjs:62-64` 无条件 `b.data.workflow.planHash = e.planHash; write(b)`。blob 派生的 brief 没有真实 `path`,`write()` 会 `mkdirSync(dirname(null))` 崩;若给它造一个 path,就等于在 fast-forward 之前往工作树落一份 untracked 副本,随后 `git merge --ff-only` 因「untracked 文件会被覆盖」直接失败(`GIT_FAST_FORWARD_FAILED`),把死结换成更坏的结。 另外,sync 的凭证模型本身依赖 brief:`executeDomain` 用 `b.data.workflow.planHash === e.planHash` 做前置校验。工作树没有 brief 时这个凭证无处存放。 ## 真修复的形状(择一,需设计决策) - **A. 给 plan 加只读声明**:域导出 `transientPlan: true` 时,`planDomain` 跳过 `write(b)`,`executeDomain` 改走 `--plan-hash` 显式凭证(与 `index rebuild`、`blog publish` 这类无 brief 域同构)。改动面:`cli.mjs` 两处 + `sync.mjs` + 文档与插件 SKILL 的 `sync execute` 调用形态(需带 `--plan-hash`,属对外契约变更,要走 CLI 版本 bump)。 - **B. 让 sync 不需要 brief**:squash 收口场景里调用者已在基线分支上,`upstream` 可直接取 `origin/<当前分支>`,`baseBranch` 无需从 brief 读。凭证改由 `--plan-hash` 承载(同 A 的持久化跳过)。这条比 A 更窄,但语义损失是:非 main 基线的 sync 要显式传 `--base-branch`。 - **C. 维持现状**:明确把「squash 合并后收口」定义为人工 `git merge --ff-only origin/<base>` + 在 main 上重跑 review,插件 SKILL §4 写清,不再假装 CLI 覆盖该路径。 我倾向 **B**(最小且不改现有凭证语义,只把无 brief 的分支场景显式化),但它仍是对外契约变更,该由你定方向后再动。 ## 已验证不受影响 - `sync fetches and fast-forwards only`、`sync blocks a dirty repository` 在本仓当前代码上 5/5 绿(浅修复已全部回退,工作区干净)。 - 关联:issue #59 记录了现象(本 issue 记录为什么不能浅修);PR #64 是另一条独立修复(bind 的 `dirHash` 遇子目录抛 `EISDIR`)。
Activity
[-][fix] squash 合并后 sync 死结:main 工作树无 brief 时 BRIEF_NOT_FOUND[/-][+][design] squash 后 sync 取不回基线:浅修复被 harness 的 readBrief+write 两处堵死,需契约变更[/+]stack-wuh commentedon Oct 12, 2026
本轮另两条修复已就绪:PR #64(bind dirHash 子目录 EISDIR)、PR #67(CLI 发布链缺工作流)。本 issue 记录的 sync 死结经实测无法浅修(抛错点在 cli.mjs 的 readBrief 与 planDomain 的无条件 write(b)),代码已全部回退、未留半成品;三个可选设计与推荐见正文。