设计提案:llmdoc Update 从「基于日期」迁移到「基于 commit 水位线」
| 项 |
值 |
| 状态 |
Proposal(提案,未实现) |
| 范围 |
/llmdoc:update + Codex llmdoc-update 及其全部镜像表面 |
| 目标分支 |
feat_batch_update |
| 创建日期 |
2026-06-29 |
| 验证方式 |
3 候选方案 → 4 维对抗审查(git 语义 / llmdoc 约束 / 多批正确性 / 迁移并发)→ 综合;多处 git 行为已对本仓库自身历史实测 |
本文档描述的是尚未落地的设计。它不是 llmdoc/ 稳定文档(稳定文档只能描述当前已存在的行为)。实现后,本文的结论应被拆分迁移到 llmdoc/architecture/update-orchestration.md、commands/update.md 等真相源,本文转为历史决策记录。
目录
- 问题诊断
- 目标与非目标
- 核心模型:单一 commit 水位线
- 状态文件设计
- 范围解析算法(校验阶梯)
- 多批 commit 支持
- net diff vs per-commit
- 模式选择重构
- 更新工作流步骤
- 边界情况表
- Agent 契约改动
- 受影响文件全清单
- 迁移与向后兼容
- 被否决的备选
- 待拍板的决策
- 风险
- 验证 / 测试计划
1. 问题诊断:为什么现在是「基于日期」
当前 /llmdoc:update(及 Codex 镜像 llmdoc-update)的变更检测锚定在三类易失信号上:
- 工作树 + 暂存区 diff(
commands/update.md:inspect the current working tree, staged changes)
- assistant 的新鲜任务记忆(
fast 模式的前提)
- 日期戳:scratch report 的
Date:、reflections 的 YYYY-MM-DD-*.md 文件名、memory/archive/YYYY-MM-DD/ 目录、skills/llmdoc/templates/stop.sh 的 date -u 时间戳
根本缺陷:系统没有「文档已同步到哪个 source commit」的持久水位线。后果:
- 只有「刚改完、上下文还新鲜」时(
fast 模式)才可靠;
- 提交后工作树变干净 → 无法重建「自上次同步以来改了什么」,只能靠人脑/日期推断;
- 无法在一次 update 里吞掉多批 commit。
这就是本提案要解决的核心问题。
2. 目标与非目标
目标
- 用一个持久、被 git 跟踪的 commit 水位线替换隐式的日期/工作树变更检测。
- 默认计算
水位线..HEAD 的 net 变更集(跨多个提交的净效果)。
- 在一次 update 运行里支持多批 commit:显式区间、显式提交列表、多个不相邻分组,「一次性 diff 并生成更新 doc」。
- 成功更新后把水位线推进到一个已提交的 SHA。
- 在非 git / shallow clone / 首跑无水位线 / rebase 后水位线失效等场景优雅降级。
非目标
- 不改变 llmdoc 的知识分层模型(
must/ overview/ architecture/ guides/ reference/ memory/ + .llmdoc-tmp/)。
- 不自动 commit(沿用「只在用户要求时才提交」)。
- 不引入可执行脚本作为承载契约(保持 markdown-first,见 §14)。
- 不做并行 recorder fan-out(见 §14)。
- session-start「落后 N 个提交」提示属于后续增强,不在本提案核心范围(见 §15)。
3. 核心模型:单一 commit 水位线
引入一个被 git 跟踪的水位线(watermark):llmdoc/ 当前已反映的「最后一个 source commit 的 SHA」。每次 update:
读水位线 W → 校验对象存在性与可达性 → 计算 net 变更集(默认 W..HEAD,可叠加多批)
→ 映射到受影响文档 → 选模式跑更新 → 成功后把 W 推进到 HEAD
未提交的工作树变更始终作为附加输入纳入,但永不推进水位线——水位线只记录已提交历史。
硬约束:durable 状态必须进 git,绝不能放 .llmdoc-tmp/(它被 gitignore、不被索引、随时可删)。
4. 状态文件设计
路径
llmdoc/state/sync.md —— 新建 llmdoc/state/ 目录,与 must/ overview/ architecture/ guides/ reference/ memory/ 平级。
为什么是这个位置(对抗审查验证):
- ❌ 不放
llmdoc/memory/:skills/llmdoc/templates/stop.sh 的 active-memory 计数会扫 memory/ 下所有文件(只排除 archive/*、lessons-learned.md、doc-gaps.md)。放这里会让归档阈值 5 永久虚高,还得打一个脆弱的 stop.sh 补丁。
- ❌ 不用 JSON(如
llmdoc/.sync-state.json):违反 markdown-first,且隐含 jq 依赖。
- ✅
llmdoc/state/ 是独立目录,声明为非知识类:recorder 不往里写文档、index.md 不把它索引为知识。它是「关于文档的同步状态」,不是项目知识。
格式
纯 markdown,一行一事实(用单条 awk/grep 即可解析,永不需要 parser)。唯一承载字段(SHA)用空行与易变字段隔开,避免 git 把它和易变行合成一个多行合并冲突块(对抗复现确认过:相邻易变行会强制多行冲突)。
# llmdoc sync state
<!-- Machine-managed by /llmdoc:update. Do not hand-edit watermark-commit
except to recover from a rebase. Never add to startup.md / MUST. Never index as knowledge. -->
- schema: 1
- watermark-commit: 7a8fe470627898d46258d8abf50b160a8e198eb8
- repo-root: 15cdb3172a8ed88ab402f19ceee52e146a120508
- watermark-subject: docs: simplify Codex install and add Chinese README link
- updated-at: 2026-06-29T00:00:00Z
- updated-by: /llmdoc:update mode=fast
上面的 SHA 为示例(7a8fe47 为撰写时的 HEAD,15cdb31 为本仓库 root 提交,已实测)。
字段
| 字段 |
作用 |
watermark-commit |
唯一承载字段,全 40-hex SHA。读取:awk -F': ' '/^- watermark-commit:/{print $2}' llmdoc/state/sync.md | tr -d '[:space:]' |
schema |
整数格式版本。未知值 ⇒ 当水位线不可读 ⇒ 走 first-run 路径并醒目告警 |
repo-root |
根提交 SHA(git rev-list --max-parents=0 HEAD | tail -1)。仅作身份校验、永不作为控制流闸门;不一致只告警 |
watermark-subject |
rebase 后的人工 sanity check 辅助(rebase 通常保留 subject) |
updated-at / updated-by |
仅调试溯源,永不参与范围计算 |
不在文件内放滚动 sync-log
对抗审查指出:「newest-first 追加日志」是每次必冲突的生成器,且与 git 历史重复。审计追踪改为依赖 git log -- llmdoc/state/sync.md。
5. 范围解析算法(校验阶梯)
所有 git plumbing 不在 set -e 下运行(或逐个捕获退出码),让 128 退出码降级而非中止整个流程。阶梯顺序固定、不可调换:
# 1. 能力探测
git rev-parse --is-inside-work-tree # != true → 非 git:回退旧的工作树检测,
# 不读/不推进水位线,报 "non-git: watermark inactive"
SHALLOW=$(git rev-parse --is-shallow-repository)
H=$(git rev-parse HEAD) # 运行开始时只取一次
# 2. 读水位线
W=$(awk -F': ' '/^- watermark-commit:/{print $2}' llmdoc/state/sync.md 2>/dev/null | tr -d '[:space:]')
# 空 / 缺失 / 未知 schema → FIRST-RUN:base=HEAD,不回填(除非 --from/--since),
# 强制 ≥ analysis,结束时在 HEAD 落水位线
# 3. ★ 先查对象存在性(绝不能先 merge-base)
git cat-file -e "$W^{commit}" 2>/dev/null # rc≠0 → 对象丢失 / GC / 低于 shallow 边界
# → SHALLOW=true 则提示 `git fetch --unshallow`(不自动 fetch);
# 按 first-run base=HEAD 处理,强制 ≥ analysis,不推进
# 4. 可达性(仅在第 3 步通过后)
git merge-base --is-ancestor "$W" "$H" # rc=0 → 水位线 VALID,RANGE_BASE=$W
# rc=1 → 不是祖先,做反向测试:
git merge-base --is-ancestor "$H" "$W" # rc2=0 → ★ HEAD 落后于水位线
# → 拒绝:不算 W..HEAD(反向区间会把「未来文件」列为删除,从而抹掉文档);
# 不倒退水位线;告警「HEAD 落后,疑似 reset/checkout 旧历史,确需则用 --from 重锚」;
# 只处理工作树
# rc2≠0 → 真分叉(rebase / squash-merge):RANGE_BASE=$(git merge-base "$W" "$H"),
# 告警可能与已记录提交重叠(recorder 幂等),强制 ≥ analysis
# 5. 默认 net 变更集(rename 感知、NUL 安全)
git diff --name-status -M -C -z "$RANGE_BASE" "$H"
# 解析 R<score> old\tnew 行:
# old 路径 → 在 llmdoc/ 中清理过期引用
# new 路径 → 反映当前状态
对抗审查在本仓库验证出的、三个候选都漏掉的 bug
git merge-base --is-ancestor 对缺失对象退出 128(fatal)而非 1。因此必须先 git cat-file -e 确认对象存在,否则一个被 GC 的水位线会让整个流程崩溃。
- HEAD 落后水位线(
git reset --hard 到旧提交 / checkout 旧 tag / CI 钉旧 SHA):W..HEAD 变成反向区间,会把「未来才有的文件」列为删除,从而抹掉文档。必须用双向 is-ancestor 检测并拒绝这种情况。
关键 git 语义说明
- 线性历史下
A..B(two-dot)与 A...B(three-dot)的 --name-only 集合相同;用 two-dot,因为第 4 步已保证/恢复 RANGE_BASE 是 HEAD 的祖先。
- two-dot net diff 折叠掉「先加后撤」「中间抖动」,正好对应 recorder 只描述现状。
6. 多批 commit 支持
/llmdoc:update 的 argument-hint 扩展为:
[summary] [--range A..B ...] [--commits SHA,SHA ...] [--since REF] [--from SHA] [--working-tree-only] [--include-default]
| 调用 |
行为 |
/llmdoc:update |
默认:watermark..HEAD net diff + 工作树 |
/llmdoc:update --range A..B --range C..D |
两个不相邻批次一次性处理:两个 name-status 集合取并集,文档对合并集更新一次 |
/llmdoc:update --commits abc,def,99a |
显式非连续提交列表(merge 提交拒绝、root 提交用空树处理) |
/llmdoc:update --since v2.0.0 或 --since 2026-05-01 |
某 tag/日期以来全部 |
/llmdoc:update --from <sha> |
仅本次重锚起点,不动状态文件 |
/llmdoc:update --working-tree-only |
完全等价今天的旧 fast 行为,永不推进水位线 |
可自由组合:/llmdoc:update --range A..B --commits zzz 把一个区间批次与一个单提交批次并起来。
处理(单趟、单 recorder,非并行 fan-out)
- 从
$ARGUMENTS / 用户消息解析所有批次 flag。
- 每批用对应的
git diff --name-status 解析出 impact 路径集:
--range A..B → git diff --name-status -M -C -z A B
--commits <sha> → 单提交对其首父;root 提交对空树(见 §10);merge 提交拒绝(见 §10)
--since <ref> → <ref>..HEAD;--since <date> → git log --since=<date> --name-only --pretty=format: | sort -u
- 最终变更集 = 所有批次路径集 ∪ 工作树(unstaged)∪ 暂存(staged)∪ 未跟踪,
sort -u。重叠幂等。
- 噪声过滤:所有 diff/rev-list 都加
:(exclude)llmdoc/state/sync.md 与 :(exclude).llmdoc-tmp/**,使每次运行重写状态文件不会自触发。
- 跑一次
fast/analysis/full 更新,覆盖整个合并集,只推进一次水位线。
优先级
只要给了 --range/--commits/--since/--from 任一,默认 W..HEAD 就不自动叠加,除非同时给 --include-default。
自然语言
「把鉴权重构那几个 commit 和上周 CI 的 commit 一起更新」视作语法糖:assistant 解析成等价 --commits/--since,先确认解析出的 SHA,再运行。
7. net diff vs per-commit
两类消费者、两种 git 输入:
recorder 消费 net diff / batch-tip 当前状态
让 llmdoc/ 匹配仓库现状,从不叙述历史(遵守现有不变量「stable docs 不得描述已不存在的行为」)。
★ 关键纠正(三个候选共有的 blocker):「当前状态」指批次 tip,用 git show <tip>:<path> 读取,只有当 batch tip == HEAD 且工作树干净时才等于磁盘内容。历史/不相邻批次绝不能读 HEAD/磁盘。
对抗审查用本仓库 README.md 实测:--commits 456c9ed 若读 HEAD,会把未请求的 7a8fe47 对 README 的改动一起卷进文档。
规则:
- 同一路径被多批触及 ⇒ 在触及它的最高批次提交处取内容;
- 若批外提交也改过该路径 ⇒ 告警「path X 有批外修改,结果可能过期」,而非静默读 HEAD;
- 当前 dirty 的路径 ⇒ 磁盘内容优先(最新意图),但标记 uncommitted、不为该路径推进水位线;只有工作树集合才允许从磁盘读。
reflector / investigator 才可消费 per-commit 历史
git log --oneline RANGE_BASE..HEAD、git show <sha>、git log --first-parent(降侧支噪声)——只用于解释「为什么」(某方案为何被回退、流程教训),永不决定改哪些文档,且仅在真要写 reflection 时使用。
在 merge-base 恢复的 baseline 上,把 reflector 的 per-commit 窗口收窄到不早于旧水位线,避免重新「学习」已被 squash 掉的历史。
写进所有提示与 update-orchestration.md 的不变量
Net diff(批次 tip 处的状态)决定改哪些文档、文档现在该怎么写(recorder)。Per-commit 历史只解释为什么、流程在哪里出问题(reflector,仅在写 reflection 时)。绝不记录 tip 处已不存在的中间状态。
8. 模式选择重构
三模式(fast/analysis/full)保留;触发器从「上下文是否新鲜」换成 范围大小 × 作者归属 × 风险。新鲜度变成模式选择的输入,而非变更检测的来源——变更检测始终基于 commit。
| 模式 |
触发(可调默认) |
| fast |
≤ 3 个自己作者的提交 且 ≤ 1 个文档簇 且能自信点名受影响文档;--working-tree-only 也默认 fast。保留「提交后立刻更新」的廉价路径 |
| analysis |
~4–15 提交,或任一非自己作者的提交(拉取他人代码后无新鲜上下文,作者归属是硬升级信号),或多簇,或 baseline 为恢复/派生/首跑。跑一次聚焦 investigator(sink=file,喂入已解析的 net-diff 路径清单),再 recorder |
| full |
> ~15 提交,或多批回填,或历史重写恢复,或事实有争议,或 reflection 重的失败。investigator + reflector + recorder 三角色分离 |
硬地板(强制 ≥ analysis):merge-base 恢复的 baseline、派生/首跑 baseline、任一非自己作者的提交(「已反映到文档」的假设此时未经证实)。
回填爆炸半径上限:首跑/--since 范围超过 ~20 提交或 ~50 文件 ⇒ 强制 full 且要求用户显式确认(「回填跨 N 提交 / M 文件,继续?」),避免在老仓库上静默产出巨型低价值重写。
阈值以可调默认陈述(「~3」「~15」);assistant 报告所选模式及触发信号;用户可显式覆盖。
9. 更新工作流步骤
- 加载
llmdoc skill;读 llmdoc/index.md、startup.md、MUST 文档、相关 guides/ 与 memory/reflections/;校验任何 .llmdoc-tmp/investigations/ 报告是否对当前 revision 仍有效。
- 解析同步状态(新):能力探测(
is-inside-work-tree、is-shallow-repository、一次性取 H=HEAD);用锚定 awk 读 llmdoc/state/sync.md 水位线;跑固定校验阶梯(先 cat-file -e、后 merge-base;HEAD-落后用双向 is-ancestor)。
- 计算变更集(新):解析批次 flag;每批用
git diff --name-status -M -C -z 解析(root 用空树、--commits 拒绝 merge);应用 :(exclude)llmdoc/state/sync.md 等 loop-breaker;与工作树+暂存+未跟踪并集;默认批次 RANGE_BASE..H。
- 处理降级场景(非 git / shallow 低于边界 / 首跑 / 孤儿 / 分叉 / HEAD-落后),见 §10;降级运行绝不伪造水位线推进。
- 按范围大小 × 作者归属 × 风险选模式;遵守硬地板与回填爆炸半径上限。
- 仅按模式需要 investigate,喂入已解析的 net-diff 路径清单;investigator 的 scratch 报告元数据记录解析出的
RANGE_BASE..H 范围。
- 仅当存在流程教训时 reflect;reflector 可读 per-commit 历史以捕获「为什么」。
- 用
index.md Routing + must/doc-routing.md + reference/repo-surfaces.md 把变更集映射到受影响文档(rename 取 old+new 双路径);recorder 仅更新受影响文档、对批次 tip 状态记录、reconcile memory/doc-gaps.md。
- 跑 active-memory 归档检查(不变;
llmdoc/state/ 在 memory/ 之外,不计入,无需改 stop.sh)。
- 同步
llmdoc/index.md;确保 llmdoc/state/sync.md 不被列为知识 / Key Document。
- 推进水位线(新,recorder 拥有的终末步):仅在消费了已提交范围的完整成功更新后;若存在任何 in-progress 操作标记或 HEAD detached 则拒绝;按连续前缀规则推进到捕获的
H(默认)或最高未断前缀 tip(部分/批次);working-tree-only/失败/HEAD-落后绝不推进;只重写 watermark-commit + updated-at + watermark-subject。
- 报告:模式、解析出的范围/批次 + N 个提交、旧→新水位线(或为何没动)、scratch/reflection 路径、归档动作、改动的稳定文档。
10. 边界情况表(已对本仓库验证)
| 情况 |
处理 |
| 非 git 仓库 |
is-inside-work-tree != true ⇒ 跳过所有 commit 逻辑,回退旧工作树+暂存+摘要检测,不读/不推进;报 "non-git: watermark inactive"。sync.md 可存在但休眠 |
| shallow clone,水位线低于边界 |
is-shallow-repository=true 且 cat-file -e $W^{commit} 失败 ⇒ 告警 + 提示 git fetch --unshallow(不自动 fetch);按 first-run base=HEAD,强制 ≥ analysis,不推进。可达则正常 |
| 首跑 / 无水位线 / schema 不可读 |
不对空/零 SHA diff,不用 git log -1 -- llmdoc/ 派生(已验证它滞后于 HEAD);base=HEAD,本次只记工作树,落水位线=HEAD,强制 ≥ analysis;总是报告所走路径。可选 --from/--since 回填 |
| rebase / squash-merge / 孤儿水位线(对象在、非祖先、HEAD 未落后) |
cat-file -e 通过、is-ancestor W H=1、反向=1 ⇒ 真分叉:RANGE_BASE=merge-base(W,H),告警可能重叠(recorder 幂等、reflector 跳过旧提交),强制 ≥ analysis,成功后推进到 HEAD |
| HEAD 落后水位线(reset --hard 旧 / checkout 旧 tag / CI 钉旧 SHA) |
is-ancestor W H=1 且反向 is-ancestor H W=0 ⇒ 拒绝:不算 W..HEAD(反向区间会抹文档)、不倒退水位线、告警用 --from 重锚、只处理工作树 |
| 孤儿水位线(对象 GC/缺失) |
cat-file -e 失败(捕获退出码,2>/dev/null)⇒ 绝不调 merge-base ⇒ first-run base=HEAD + 醒目告警,强制 analysis,成功后在 HEAD 重写 |
W == HEAD(已同步) |
rev-list --count=0 且 net diff 空;若无工作树变更且无 flag ⇒ 报 "already up to date through ",不动文档/水位线直接退出 |
范围内只动 llmdoc/(之前的文档/状态提交) |
git diff --name-only RANGE_BASE..HEAD -- ':(exclude)llmdoc/**' 为空 ⇒ 快进水位线到 HEAD、零文档工作,打破文档提交自引用环。混合 source+doc 提交不跳过(存在非 llmdoc 路径) |
--commits 含 merge 提交 |
父数 > 1 ⇒ 拒绝(<sha>^! / show --name-only 对 merge 为空,会静默贡献 0 路径),提示改传 A..B |
--commits 含 root 提交 |
git rev-parse --verify --quiet <sha>^ 失败 ⇒ 对空树 diff:git hash-object -t tree /dev/null(= 4b825dc642cb6eb9a060e54bf8d69288fbee4904,已验证返回 root 的全部文件;<sha>^! 对 root 不可靠) |
| detached HEAD / rebase 中 / merge 中 / cherry-pick / revert 中 |
HEAD 经 rev-parse HEAD 可解析,范围 math 可算;但若 .git/MERGE_HEAD、CHERRY_PICK_HEAD、REVERT_HEAD、rebase-merge/、rebase-apply/(用 git rev-parse --git-path 探测)任一存在或 HEAD plain-detached ⇒ 拒绝推进水位线(detached 落的水位线可能变不可达),告警先完成操作 / 提交到分支 |
| 路径在已提交批次与 dirty 工作树都被改 |
磁盘内容赢(最新意图),标 uncommitted,不为该路径推进水位线。干净的已提交批次路径在批次 tip 读 |
| sync.md 被手改坏 / 未知 schema / 短 SHA |
锚定 awk 读、去空白、git rev-parse --verify --quiet 规范化;非全 SHA ⇒ 当不可读走 first-run + 醒目「watermark reset」告警;永不崩溃。写入永远存全 40-hex |
| 并发分支都推进了水位线(合并冲突) |
单 SHA 行使冲突最小;解决规则:取后代 SHA(is-ancestor A B 判定);若互不为祖先取 merge-base(A,B)(重处理幂等)。repo-root 字段对来自无关历史的水位线告警(advisory,非硬闸) |
| 部分 / 失败更新 |
不推进。水位线只在完整成功、消费了已提交范围时才动 |
11. Agent 契约改动
agents/investigator.md:update 场景下基于预算 net-diff 路径清单工作(git diff --name-status -M -C);sink=file 元数据加 Range: 与 Watermark:,复用时校验「当前范围是否为报告范围子集」(否则失效);per-commit 仅用于解释意图、不作文档内容。
agents/recorder.md:从 net diff 推理、用 git show <tip>:<path> 读批次 tip内容(除 uncommitted 工作树外绝不读 HEAD/磁盘);不记录 tip 处不存在的中间状态;拥有 llmdoc/state/sync.md,作为成功更新的终末步推进水位线;working-tree-only/失败/部分/HEAD-落后绝不推进;保持 sync.md 不进 index.md 知识目录;index.md 与水位线单写者。
agents/reflector.md:可读 per-commit 历史(git log/show 跨已同步范围,--first-parent 降噪)以捕获「为什么」、发现回退/返工;merge-base 恢复的 baseline 上忽略旧水位线之前的提交。
agents/worker.md:加单写者说明——绝不编辑 index.md / llmdoc/state/sync.md。
.codex/agents/llmdoc-investigator.toml:镜像 net-diff 路径清单契约 + scratch 范围元数据。
.codex/agents/llmdoc-recorder.toml:镜像 sync.md 所有权、批次 tip 内容规则、水位线推进终末步、绝不推进条件、保持不索引规则。
.codex/agents/llmdoc-reflector.toml:镜像 per-commit 历史说明(该 toml 确实存在,与 llmdoc-worker.toml 一起共 4 个 toml)。
新增共享不变量(写进所有角色提示与 architecture/update-orchestration.md):durable 同步状态只存于被跟踪的 llmdoc/state/sync.md;水位线记录已提交历史(一个已提交 SHA);未提交工作树变更是附加输入、永不推进它;index.md 与水位线每次运行恰有一个写者。
12. 受影响文件全清单(Claude+Codex 双表面)
guides/updating-update-workflow.md 已规定「改 update 必须跨表面同步」。跨 ~22 个表面的镜像漂移是本提案最大的维护风险,必须一并改。
入口契约
| 路径 |
改动 |
commands/update.md |
新增 Sync State 章节(路径/格式/读+推进规则 + 校验阶梯的精确内联 git 命令);argument-hint 加批次 flag;插入 resolve-state / compute-range / advance-watermark 三个 Action;Mode Selection 重锚到范围大小+作者归属;加降级模式 + loop-breaker + 批次 tip 内容规则 |
skills/llmdoc-update/SKILL.md |
为 Codex 镜像以上全部(同 flag、文件、阶梯、步骤),从 $ARGUMENTS / 用户消息读 flag |
方法论 / 设计
| 路径 |
改动 |
skills/llmdoc/references/update-and-memory.md |
加 commit 水位线模型、llmdoc/state/sync.md 契约、net-diff vs per-commit(批次 tip)规则;Memory ownership 下把 sync.md 归 recorder |
llmdoc/architecture/update-orchestration.md |
加水位线进 Knowledge Layers + Invariants(durable tracked markdown;绝不进 .llmdoc-tmp/;只推进到已提交 SHA;工作树不推进;可达性门控;单写者;不作知识索引);记录解析阶梯、two-dot 默认、连续前缀推进,以及被否决的备选 |
Agents
agents/{investigator,recorder,reflector,worker}.md + .codex/agents/llmdoc-{investigator,recorder,reflector}.toml(改动见 §11)。
新文件 + Init 落种子
| 路径 |
改动 |
llmdoc/state/sync.md |
新建被跟踪文件;本仓库随引入提交预置指向该提交(dogfood,老用户升级即有水位线) |
commands/init.md |
init 时创建 llmdoc/state/sync.md,watermark-commit=HEAD、repo-root 落种子,使首次 update 已有有效锚点 |
skills/llmdoc-init/SKILL.md |
为 Codex 镜像 init 落种子 |
skills/llmdoc/references/templates.md |
加 llmdoc/state/sync.md 模板块供 init 脚手架 |
文档 / 索引
| 路径 |
改动 |
llmdoc/guides/updating-update-workflow.md |
Main Steps 加新 flag、sync.md 语义、水位线推进;mirror 步骤补 reflector + worker toml;Common Failure Points 加(把 git-log 派生当真相、对未提交/HEAD-落后推进、先 merge-base 后 cat-file、为历史批次读 HEAD、two-dot 反向区间);Verification 加(只推进到已提交 SHA、working-tree-only 永不推进、sync.md 不被索引、manifest description 也要改而非仅版本) |
llmdoc/reference/repo-surfaces.md |
登记 llmdoc/state/sync.md 为被跟踪的 durable 同步状态表面与真相源 |
llmdoc/index.md |
把 llmdoc/state/ 列为非知识、机器管理的状态目录(一行说明)+ Routing「改水位线/范围逻辑前先读 update-orchestration」;加不变量「index 永不把水位线索引为知识」。不把 sync.md 列为 Key Document |
llmdoc/must/doc-routing.md |
加 Read-Next:把 /llmdoc:update 范围/水位线改动路由到 update-orchestration + updating-update-workflow(现有条目已指向,扩展以点名水位线) |
llmdoc/memory/doc-gaps.md |
关掉「.codex/agents toml 落后于 prose」旧 gap(镜像后);加新 gap:无可执行测试/fixture 验证 range/watermark math(rebase/squash/shallow/非 git/HEAD-落后) |
公开面 + 清单
| 路径 |
改动 |
README.md |
Public Surface + /llmdoc:update 章节:说明 commit-based update、水位线文件、批次 flag、优雅降级 |
README.zh-CN.md |
中文镜像 README.md 改动(公开接口 + /llmdoc:update) |
.claude-plugin/plugin.json |
升版本 且更新 description 提到 commit-watermark update(不能只改版本号) |
.codex-plugin/plugin.json |
升版本 且更新 description / longDescription,措辞与 Claude manifest 平行 |
.gitignore |
不变。列出以使决定显式:llmdoc/state/ 保持被跟踪,只忽略 .llmdoc-tmp/ |
13. 迁移与向后兼容
- 新 init going forward:
commands/init.md + skills/llmdoc-init/SKILL.md 在 bootstrap 时落 watermark-commit=HEAD、repo-root,新仓库开局即同步,首次 /llmdoc:update 已有有效锚点。
- 已有 llmdoc 仓库升级(有
llmdoc/ 无 llmdoc/state/sync.md):首次 update 检测到文件缺失 ⇒ 走 first-run = 在当前 HEAD 落种子、不历史回填(前提:现有文档已反映到 HEAD,这正是「维护中的文档」的语义),本次只记工作树变更,再写水位线=HEAD。强制 ≥ analysis(假设未经证实),报告 "seeded baseline at HEAD ; documenting working tree only"。
- 可选回填:首跑加
--from <已知好提交> 或 --since <tag>,diff base..HEAD(analysis/full,受爆炸半径上限),追溯吸收后写水位线=HEAD,报告 "backfilled from "。
- Dogfood:本仓库随引入提案的提交预置 sync.md 指向该提交,升级用户直接拿到显式水位线,而非依赖首跑派生。
- 原子性(鸡生蛋):水位线写入是普通的被跟踪编辑,与文档改动在同一个用户提交里(workflow 从不自动 commit)。它记录运行开始时捕获的 HEAD;下一次运行
旧HEAD..文档提交 全是 llmdoc/ 路径,:(exclude)llmdoc/** loop-breaker 快进、零文档工作。若用户漏改水位线就提交文档,下次靠同样的 llmdoc-only 快进自愈。
- 向后兼容:date-stamped scratch/reflection/archive 目录不变;
--working-tree-only 完整保留旧 fast 行为;唯一新增 durable 物件就是 llmdoc/state/sync.md。
14. 被否决的备选(及理由)
- ❌ JSON 状态文件(
llmdoc/.sync-state.json):破坏 markdown-first,隐含 jq 依赖。
- ❌ git notes / tag 存水位线:不在 PR diff 里可见、push 需额外配置、易丢。
- ❌ 纯派生(把「last commit touching
llmdoc/」当真相):已验证滞后 HEAD,且把「碰过文档」与「同步到此源状态」混为一谈。仅作 first-run 的可选回退,不作主路径。
- ❌
llmdoc-range.sh 助手脚本:(1) 破坏 markdown-first,让可执行 shell 成为承载契约;(2) 放 skills/llmdoc/templates/ 会被 init 复制进每个用户仓库,成为无主、版本漂移的代码面;(3) 给最易错的逻辑加未测试代码;(4) 还得双表面镜像(反而增面)。范围 git 命令以文档化的精确命令写在 command + skill 里作单一真相。若未来仍要助手脚本,它不能放 templates/(用不被复制的位置),必须登记进 reference/repo-surfaces.md 与 updating-update-workflow.md 镜像清单,并附带覆盖 rebase/squash/shallow/非 git/HEAD-落后/root/merge 的测试 fixture。
- ❌ 并行 recorder fan-out:对共享
index.md / doc-gaps.md 的无保护并发写。坚持单 recorder 写者。
- ❌ 文件内滚动 sync-log:每次必冲突,且与 git 历史重复。审计靠
git log -- llmdoc/state/sync.md。
15. 待拍板的决策(含推荐默认)
- 升降级阈值(fast→analysis→full 的提交/文件数)。推荐默认:≤3 个自作者提交 ⇒ fast 可选;~4–15 或任一他人提交 ⇒ analysis;>15 或多批回填或重写恢复 ⇒ full。作可调指引,非硬闸。
- 回填爆炸半径确认阈值。推荐默认:首跑/
--since 超 ~20 提交或 ~50 文件时,处理前向用户显式确认。
- 是否为
llmdoc/state/sync.md 配 .gitattributes merge driver(descendant-wins,else merge-base)。推荐默认:初期不配——单 SHA 行格式 + 文档化的「后代赢」规则对常见场景足够;并发分支冲突频发再加(merge driver 需每个 clone 注册 git config,.gitattributes 单独不保证生效,增加摩擦)。
- session-start hook 是否提示「文档落后 HEAD N 个提交」。推荐默认:留后续。若做,
rev-list --count 要 is-shallow-repository=false 守卫(否则报 "unavailable (shallow)")、排除 :(exclude)llmdoc/**、所有 git 调用 || true 包裹以免破坏会话。不属核心重构。
- 是否保留任何文件内审计日志。推荐默认:不留——靠
git log -- llmdoc/state/sync.md(避免每次运行的合并冲突噪声)。
16. 风险
- 跨 ~22 表面的镜像漂移:
guide 早就警告的失败模式,现在面更大(flag + 状态文件)。靠扩展 updating-update-workflow.md 清单缓解,这是最大维护风险。
- 批次 tip 内容选择比读 HEAD 复杂:若后来维护者「简化」回读 HEAD/磁盘,不相邻批次正确性 bug 复发(已用 README.md 重叠验证)。必须在 recorder 提示里写成硬不变量。
- 水位线指向「文档提交之前」的不变量较微妙:未来维护者若让 recorder 编辑后重读 HEAD,或把文档单独提交,可能触发重扫。靠
:(exclude)llmdoc/** loop-breaker 在 update-orchestration.md 与角色提示里显式声明缓解。
- shallow / CI clone 常见:水位线早于 clone 深度时静默回退工作树检测、可能少记。靠告警 +
--unshallow 提示 + 拒绝推进缓解,但用户可能忽略告警。
- range/watermark math 无可执行测试(rebase/squash/shallow/非 git/HEAD-落后/root/merge):最易错的逻辑未被验证。已登记为新 doc-gap;靠本提案中已实测的 git 命令行为缓解。
- markdown grep/awk 解析:对单 SHA 行健壮,但若有人重排文件格式则脆弱。靠 schema 字段、机器管理 banner、读时
rev-parse 规范化缓解。
repo-root 身份在从根重写历史(filter-repo)时失效:罕见,落入孤儿/分叉路径降级为 base=HEAD。保持 advisory,永不阻塞。
17. 验证 / 测试计划
实现完成后应满足(对应 updating-update-workflow.md 的 Verification 扩展):
/llmdoc:update 与 Codex llmdoc-update 描述相同的批次 flag 与水位线语义。
- 水位线只推进到一个已提交的 SHA;
--working-tree-only / 失败 / HEAD-落后永不推进。
llmdoc/state/sync.md 被 git 跟踪、不被 index.md 索引为知识、不进 startup/MUST。
- 校验阶梯顺序为:能力探测 → 读水位线 →
cat-file -e → 双向 is-ancestor → 范围。
- 两个 plugin manifest 的 version 与 description 都更新。
- 针对以下场景的人工或脚本化冒烟(建议补 fixture,见 doc-gap):
- 干净线性
W..HEAD;
- 多批
--range A..B --range C..D;
--commits 含 root / 含 merge(应拒绝);
- rebase 后孤儿水位线(merge-base 恢复);
git reset --hard 到旧提交(HEAD-落后,应拒绝);
- shallow clone 水位线越界;
- 非 git 目录(回退旧行为);
- 只动
llmdoc/ 的提交(应快进、零文档工作)。
本提案由多方案对比 + 对抗式边界审查综合而成;多处 git 行为已对本仓库历史实测。实现时建议先用 EnterPlanMode 把「改哪 22 个文件、什么顺序、怎么验证」定成可执行计划。
设计提案:llmdoc Update 从「基于日期」迁移到「基于 commit 水位线」
/llmdoc:update+ Codexllmdoc-update及其全部镜像表面feat_batch_update目录
1. 问题诊断:为什么现在是「基于日期」
当前
/llmdoc:update(及 Codex 镜像llmdoc-update)的变更检测锚定在三类易失信号上:commands/update.md:inspect the current working tree, staged changes)fast模式的前提)Date:、reflections 的YYYY-MM-DD-*.md文件名、memory/archive/YYYY-MM-DD/目录、skills/llmdoc/templates/stop.sh的date -u时间戳根本缺陷:系统没有「文档已同步到哪个 source commit」的持久水位线。后果:
fast模式)才可靠;这就是本提案要解决的核心问题。
2. 目标与非目标
目标
水位线..HEAD的 net 变更集(跨多个提交的净效果)。非目标
must/ overview/ architecture/ guides/ reference/ memory/+.llmdoc-tmp/)。3. 核心模型:单一 commit 水位线
引入一个被 git 跟踪的水位线(watermark):
llmdoc/当前已反映的「最后一个 source commit 的 SHA」。每次 update:未提交的工作树变更始终作为附加输入纳入,但永不推进水位线——水位线只记录已提交历史。
4. 状态文件设计
路径
llmdoc/state/sync.md—— 新建llmdoc/state/目录,与must/ overview/ architecture/ guides/ reference/ memory/平级。为什么是这个位置(对抗审查验证):
llmdoc/memory/:skills/llmdoc/templates/stop.sh的 active-memory 计数会扫memory/下所有文件(只排除archive/*、lessons-learned.md、doc-gaps.md)。放这里会让归档阈值 5 永久虚高,还得打一个脆弱的stop.sh补丁。llmdoc/.sync-state.json):违反 markdown-first,且隐含jq依赖。llmdoc/state/是独立目录,声明为非知识类:recorder 不往里写文档、index.md不把它索引为知识。它是「关于文档的同步状态」,不是项目知识。格式
纯 markdown,一行一事实(用单条
awk/grep即可解析,永不需要 parser)。唯一承载字段(SHA)用空行与易变字段隔开,避免 git 把它和易变行合成一个多行合并冲突块(对抗复现确认过:相邻易变行会强制多行冲突)。字段
watermark-commitawk -F': ' '/^- watermark-commit:/{print $2}' llmdoc/state/sync.md | tr -d '[:space:]'schemarepo-rootgit rev-list --max-parents=0 HEAD | tail -1)。仅作身份校验、永不作为控制流闸门;不一致只告警watermark-subjectupdated-at/updated-by不在文件内放滚动 sync-log
对抗审查指出:「newest-first 追加日志」是每次必冲突的生成器,且与 git 历史重复。审计追踪改为依赖
git log -- llmdoc/state/sync.md。5. 范围解析算法(校验阶梯)
所有 git plumbing 不在
set -e下运行(或逐个捕获退出码),让128退出码降级而非中止整个流程。阶梯顺序固定、不可调换:对抗审查在本仓库验证出的、三个候选都漏掉的 bug
git merge-base --is-ancestor对缺失对象退出128(fatal)而非1。因此必须先git cat-file -e确认对象存在,否则一个被 GC 的水位线会让整个流程崩溃。git reset --hard到旧提交 / checkout 旧 tag / CI 钉旧 SHA):W..HEAD变成反向区间,会把「未来才有的文件」列为删除,从而抹掉文档。必须用双向is-ancestor检测并拒绝这种情况。关键 git 语义说明
A..B(two-dot)与A...B(three-dot)的--name-only集合相同;用 two-dot,因为第 4 步已保证/恢复RANGE_BASE是HEAD的祖先。6. 多批 commit 支持
/llmdoc:update的argument-hint扩展为:/llmdoc:updatewatermark..HEADnet diff + 工作树/llmdoc:update --range A..B --range C..D/llmdoc:update --commits abc,def,99a/llmdoc:update --since v2.0.0或--since 2026-05-01/llmdoc:update --from <sha>/llmdoc:update --working-tree-onlyfast行为,永不推进水位线可自由组合:
/llmdoc:update --range A..B --commits zzz把一个区间批次与一个单提交批次并起来。处理(单趟、单 recorder,非并行 fan-out)
$ARGUMENTS/ 用户消息解析所有批次 flag。git diff --name-status解析出 impact 路径集:--range A..B→git diff --name-status -M -C -z A B--commits <sha>→ 单提交对其首父;root 提交对空树(见 §10);merge 提交拒绝(见 §10)--since <ref>→<ref>..HEAD;--since <date>→git log --since=<date> --name-only --pretty=format: | sort -usort -u。重叠幂等。:(exclude)llmdoc/state/sync.md与:(exclude).llmdoc-tmp/**,使每次运行重写状态文件不会自触发。fast/analysis/full更新,覆盖整个合并集,只推进一次水位线。优先级
只要给了
--range/--commits/--since/--from任一,默认W..HEAD就不自动叠加,除非同时给--include-default。自然语言
「把鉴权重构那几个 commit 和上周 CI 的 commit 一起更新」视作语法糖:assistant 解析成等价
--commits/--since,先确认解析出的 SHA,再运行。7. net diff vs per-commit
两类消费者、两种 git 输入:
recorder 消费 net diff / batch-tip 当前状态
让
llmdoc/匹配仓库现状,从不叙述历史(遵守现有不变量「stable docs 不得描述已不存在的行为」)。★ 关键纠正(三个候选共有的 blocker):「当前状态」指批次 tip,用
git show <tip>:<path>读取,只有当 batch tip == HEAD 且工作树干净时才等于磁盘内容。历史/不相邻批次绝不能读 HEAD/磁盘。规则:
reflector / investigator 才可消费 per-commit 历史
git log --oneline RANGE_BASE..HEAD、git show <sha>、git log --first-parent(降侧支噪声)——只用于解释「为什么」(某方案为何被回退、流程教训),永不决定改哪些文档,且仅在真要写 reflection 时使用。在 merge-base 恢复的 baseline 上,把 reflector 的 per-commit 窗口收窄到不早于旧水位线,避免重新「学习」已被 squash 掉的历史。
写进所有提示与
update-orchestration.md的不变量8. 模式选择重构
三模式(
fast/analysis/full)保留;触发器从「上下文是否新鲜」换成 范围大小 × 作者归属 × 风险。新鲜度变成模式选择的输入,而非变更检测的来源——变更检测始终基于 commit。--working-tree-only也默认 fast。保留「提交后立刻更新」的廉价路径sink=file,喂入已解析的 net-diff 路径清单),再 recorder硬地板(强制 ≥
analysis):merge-base 恢复的 baseline、派生/首跑 baseline、任一非自己作者的提交(「已反映到文档」的假设此时未经证实)。回填爆炸半径上限:首跑/
--since范围超过 ~20 提交或 ~50 文件 ⇒ 强制full且要求用户显式确认(「回填跨 N 提交 / M 文件,继续?」),避免在老仓库上静默产出巨型低价值重写。阈值以可调默认陈述(「~3」「~15」);assistant 报告所选模式及触发信号;用户可显式覆盖。
9. 更新工作流步骤
llmdocskill;读llmdoc/index.md、startup.md、MUST 文档、相关guides/与memory/reflections/;校验任何.llmdoc-tmp/investigations/报告是否对当前 revision 仍有效。is-inside-work-tree、is-shallow-repository、一次性取H=HEAD);用锚定awk读llmdoc/state/sync.md水位线;跑固定校验阶梯(先cat-file -e、后merge-base;HEAD-落后用双向is-ancestor)。git diff --name-status -M -C -z解析(root 用空树、--commits拒绝 merge);应用:(exclude)llmdoc/state/sync.md等 loop-breaker;与工作树+暂存+未跟踪并集;默认批次RANGE_BASE..H。RANGE_BASE..H范围。index.mdRouting +must/doc-routing.md+reference/repo-surfaces.md把变更集映射到受影响文档(rename 取 old+new 双路径);recorder 仅更新受影响文档、对批次 tip 状态记录、reconcilememory/doc-gaps.md。llmdoc/state/在memory/之外,不计入,无需改stop.sh)。llmdoc/index.md;确保llmdoc/state/sync.md不被列为知识 / Key Document。H(默认)或最高未断前缀 tip(部分/批次);working-tree-only/失败/HEAD-落后绝不推进;只重写watermark-commit+updated-at+watermark-subject。10. 边界情况表(已对本仓库验证)
is-inside-work-tree != true⇒ 跳过所有 commit 逻辑,回退旧工作树+暂存+摘要检测,不读/不推进;报 "non-git: watermark inactive"。sync.md 可存在但休眠is-shallow-repository=true且cat-file -e $W^{commit}失败 ⇒ 告警 + 提示git fetch --unshallow(不自动 fetch);按 first-run base=HEAD,强制 ≥ analysis,不推进。可达则正常git log -1 -- llmdoc/派生(已验证它滞后于 HEAD);base=HEAD,本次只记工作树,落水位线=HEAD,强制 ≥ analysis;总是报告所走路径。可选--from/--since回填cat-file -e通过、is-ancestor W H=1、反向=1 ⇒ 真分叉:RANGE_BASE=merge-base(W,H),告警可能重叠(recorder 幂等、reflector 跳过旧提交),强制 ≥ analysis,成功后推进到 HEADis-ancestor W H=1 且反向is-ancestor H W=0 ⇒ 拒绝:不算W..HEAD(反向区间会抹文档)、不倒退水位线、告警用--from重锚、只处理工作树cat-file -e失败(捕获退出码,2>/dev/null)⇒ 绝不调 merge-base ⇒ first-run base=HEAD + 醒目告警,强制 analysis,成功后在 HEAD 重写W == HEAD(已同步)rev-list --count=0 且 net diff 空;若无工作树变更且无 flag ⇒ 报 "already up to date through ",不动文档/水位线直接退出llmdoc/(之前的文档/状态提交)git diff --name-only RANGE_BASE..HEAD -- ':(exclude)llmdoc/**'为空 ⇒ 快进水位线到 HEAD、零文档工作,打破文档提交自引用环。混合 source+doc 提交不跳过(存在非 llmdoc 路径)--commits含 merge 提交<sha>^!/show --name-only对 merge 为空,会静默贡献 0 路径),提示改传A..B--commits含 root 提交git rev-parse --verify --quiet <sha>^失败 ⇒ 对空树 diff:git hash-object -t tree /dev/null(=4b825dc642cb6eb9a060e54bf8d69288fbee4904,已验证返回 root 的全部文件;<sha>^!对 root 不可靠)rev-parse HEAD可解析,范围 math 可算;但若.git/MERGE_HEAD、CHERRY_PICK_HEAD、REVERT_HEAD、rebase-merge/、rebase-apply/(用git rev-parse --git-path探测)任一存在或 HEAD plain-detached ⇒ 拒绝推进水位线(detached 落的水位线可能变不可达),告警先完成操作 / 提交到分支git rev-parse --verify --quiet规范化;非全 SHA ⇒ 当不可读走 first-run + 醒目「watermark reset」告警;永不崩溃。写入永远存全 40-hexis-ancestor A B判定);若互不为祖先取merge-base(A,B)(重处理幂等)。repo-root字段对来自无关历史的水位线告警(advisory,非硬闸)11. Agent 契约改动
agents/investigator.md:update 场景下基于预算 net-diff 路径清单工作(git diff --name-status -M -C);sink=file元数据加Range:与Watermark:,复用时校验「当前范围是否为报告范围子集」(否则失效);per-commit 仅用于解释意图、不作文档内容。agents/recorder.md:从 net diff 推理、用git show <tip>:<path>读批次 tip内容(除 uncommitted 工作树外绝不读 HEAD/磁盘);不记录 tip 处不存在的中间状态;拥有llmdoc/state/sync.md,作为成功更新的终末步推进水位线;working-tree-only/失败/部分/HEAD-落后绝不推进;保持 sync.md 不进index.md知识目录;index.md与水位线单写者。agents/reflector.md:可读 per-commit 历史(git log/show跨已同步范围,--first-parent降噪)以捕获「为什么」、发现回退/返工;merge-base 恢复的 baseline 上忽略旧水位线之前的提交。agents/worker.md:加单写者说明——绝不编辑index.md/llmdoc/state/sync.md。.codex/agents/llmdoc-investigator.toml:镜像 net-diff 路径清单契约 + scratch 范围元数据。.codex/agents/llmdoc-recorder.toml:镜像 sync.md 所有权、批次 tip 内容规则、水位线推进终末步、绝不推进条件、保持不索引规则。.codex/agents/llmdoc-reflector.toml:镜像 per-commit 历史说明(该 toml 确实存在,与llmdoc-worker.toml一起共 4 个 toml)。新增共享不变量(写进所有角色提示与
architecture/update-orchestration.md):durable 同步状态只存于被跟踪的llmdoc/state/sync.md;水位线记录已提交历史(一个已提交 SHA);未提交工作树变更是附加输入、永不推进它;index.md与水位线每次运行恰有一个写者。12. 受影响文件全清单(Claude+Codex 双表面)
guides/updating-update-workflow.md已规定「改 update 必须跨表面同步」。跨 ~22 个表面的镜像漂移是本提案最大的维护风险,必须一并改。入口契约
commands/update.mdargument-hint加批次 flag;插入 resolve-state / compute-range / advance-watermark 三个 Action;Mode Selection 重锚到范围大小+作者归属;加降级模式 + loop-breaker + 批次 tip 内容规则skills/llmdoc-update/SKILL.md$ARGUMENTS/ 用户消息读 flag方法论 / 设计
skills/llmdoc/references/update-and-memory.mdllmdoc/state/sync.md契约、net-diff vs per-commit(批次 tip)规则;Memory ownership 下把 sync.md 归 recorderllmdoc/architecture/update-orchestration.md.llmdoc-tmp/;只推进到已提交 SHA;工作树不推进;可达性门控;单写者;不作知识索引);记录解析阶梯、two-dot 默认、连续前缀推进,以及被否决的备选Agents
agents/{investigator,recorder,reflector,worker}.md+.codex/agents/llmdoc-{investigator,recorder,reflector}.toml(改动见 §11)。新文件 + Init 落种子
llmdoc/state/sync.mdcommands/init.mdllmdoc/state/sync.md,watermark-commit=HEAD、repo-root落种子,使首次 update 已有有效锚点skills/llmdoc-init/SKILL.mdskills/llmdoc/references/templates.mdllmdoc/state/sync.md模板块供 init 脚手架文档 / 索引
llmdoc/guides/updating-update-workflow.mdllmdoc/reference/repo-surfaces.mdllmdoc/state/sync.md为被跟踪的 durable 同步状态表面与真相源llmdoc/index.mdllmdoc/state/列为非知识、机器管理的状态目录(一行说明)+ Routing「改水位线/范围逻辑前先读 update-orchestration」;加不变量「index 永不把水位线索引为知识」。不把 sync.md 列为 Key Documentllmdoc/must/doc-routing.md/llmdoc:update范围/水位线改动路由到 update-orchestration + updating-update-workflow(现有条目已指向,扩展以点名水位线)llmdoc/memory/doc-gaps.md公开面 + 清单
README.md/llmdoc:update章节:说明 commit-based update、水位线文件、批次 flag、优雅降级README.zh-CN.md/llmdoc:update).claude-plugin/plugin.json.codex-plugin/plugin.json.gitignorellmdoc/state/保持被跟踪,只忽略.llmdoc-tmp/13. 迁移与向后兼容
commands/init.md+skills/llmdoc-init/SKILL.md在 bootstrap 时落watermark-commit=HEAD、repo-root,新仓库开局即同步,首次/llmdoc:update已有有效锚点。llmdoc/无llmdoc/state/sync.md):首次 update 检测到文件缺失 ⇒ 走 first-run = 在当前 HEAD 落种子、不历史回填(前提:现有文档已反映到 HEAD,这正是「维护中的文档」的语义),本次只记工作树变更,再写水位线=HEAD。强制 ≥ analysis(假设未经证实),报告 "seeded baseline at HEAD ; documenting working tree only"。--from <已知好提交>或--since <tag>,diffbase..HEAD(analysis/full,受爆炸半径上限),追溯吸收后写水位线=HEAD,报告 "backfilled from "。旧HEAD..文档提交全是llmdoc/路径,:(exclude)llmdoc/**loop-breaker 快进、零文档工作。若用户漏改水位线就提交文档,下次靠同样的 llmdoc-only 快进自愈。--working-tree-only完整保留旧 fast 行为;唯一新增 durable 物件就是llmdoc/state/sync.md。14. 被否决的备选(及理由)
llmdoc/.sync-state.json):破坏 markdown-first,隐含jq依赖。llmdoc/」当真相):已验证滞后 HEAD,且把「碰过文档」与「同步到此源状态」混为一谈。仅作 first-run 的可选回退,不作主路径。llmdoc-range.sh助手脚本:(1) 破坏 markdown-first,让可执行 shell 成为承载契约;(2) 放skills/llmdoc/templates/会被 init 复制进每个用户仓库,成为无主、版本漂移的代码面;(3) 给最易错的逻辑加未测试代码;(4) 还得双表面镜像(反而增面)。范围 git 命令以文档化的精确命令写在 command + skill 里作单一真相。若未来仍要助手脚本,它不能放templates/(用不被复制的位置),必须登记进reference/repo-surfaces.md与updating-update-workflow.md镜像清单,并附带覆盖 rebase/squash/shallow/非 git/HEAD-落后/root/merge 的测试 fixture。index.md/doc-gaps.md的无保护并发写。坚持单 recorder 写者。git log -- llmdoc/state/sync.md。15. 待拍板的决策(含推荐默认)
--since超 ~20 提交或 ~50 文件时,处理前向用户显式确认。llmdoc/state/sync.md配.gitattributesmerge driver(descendant-wins,else merge-base)。推荐默认:初期不配——单 SHA 行格式 + 文档化的「后代赢」规则对常见场景足够;并发分支冲突频发再加(merge driver 需每个 clone 注册 git config,.gitattributes单独不保证生效,增加摩擦)。rev-list --count要is-shallow-repository=false守卫(否则报 "unavailable (shallow)")、排除:(exclude)llmdoc/**、所有 git 调用|| true包裹以免破坏会话。不属核心重构。git log -- llmdoc/state/sync.md(避免每次运行的合并冲突噪声)。16. 风险
guide早就警告的失败模式,现在面更大(flag + 状态文件)。靠扩展updating-update-workflow.md清单缓解,这是最大维护风险。:(exclude)llmdoc/**loop-breaker 在update-orchestration.md与角色提示里显式声明缓解。--unshallow提示 + 拒绝推进缓解,但用户可能忽略告警。rev-parse规范化缓解。repo-root身份在从根重写历史(filter-repo)时失效:罕见,落入孤儿/分叉路径降级为 base=HEAD。保持 advisory,永不阻塞。17. 验证 / 测试计划
实现完成后应满足(对应
updating-update-workflow.md的 Verification 扩展):/llmdoc:update与 Codexllmdoc-update描述相同的批次 flag 与水位线语义。--working-tree-only/ 失败 / HEAD-落后永不推进。llmdoc/state/sync.md被 git 跟踪、不被index.md索引为知识、不进 startup/MUST。cat-file -e→ 双向is-ancestor→ 范围。W..HEAD;--range A..B --range C..D;--commits含 root / 含 merge(应拒绝);git reset --hard到旧提交(HEAD-落后,应拒绝);llmdoc/的提交(应快进、零文档工作)。本提案由多方案对比 + 对抗式边界审查综合而成;多处 git 行为已对本仓库历史实测。实现时建议先用
EnterPlanMode把「改哪 22 个文件、什么顺序、怎么验证」定成可执行计划。