Skip to content

【Feat Request】设计提案:llmdoc Update 从「基于日期」迁移到「基于 commit 水位线」 #29

Description

@wh1teAlter

设计提案: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.mdcommands/update.md 等真相源,本文转为历史决策记录。


目录

  1. 问题诊断
  2. 目标与非目标
  3. 核心模型:单一 commit 水位线
  4. 状态文件设计
  5. 范围解析算法(校验阶梯)
  6. 多批 commit 支持
  7. net diff vs per-commit
  8. 模式选择重构
  9. 更新工作流步骤
  10. 边界情况表
  11. Agent 契约改动
  12. 受影响文件全清单
  13. 迁移与向后兼容
  14. 被否决的备选
  15. 待拍板的决策
  16. 风险
  17. 验证 / 测试计划

1. 问题诊断:为什么现在是「基于日期」

当前 /llmdoc:update(及 Codex 镜像 llmdoc-update)的变更检测锚定在三类易失信号上:

  • 工作树 + 暂存区 diffcommands/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.shdate -u 时间戳

根本缺陷:系统没有「文档已同步到哪个 source commit」的持久水位线。后果:

  • 只有「刚改完、上下文还新鲜」时(fast 模式)才可靠;
  • 提交后工作树变干净 → 无法重建「自上次同步以来改了什么」,只能靠人脑/日期推断;
  • 无法在一次 update 里吞掉多批 commit

这就是本提案要解决的核心问题。


2. 目标与非目标

目标

  • 用一个持久、被 git 跟踪的 commit 水位线替换隐式的日期/工作树变更检测。
  • 默认计算 水位线..HEADnet 变更集(跨多个提交的净效果)。
  • 一次 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.mddoc-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

  1. git merge-base --is-ancestor 对缺失对象退出 128(fatal)而非 1。因此必须git cat-file -e 确认对象存在,否则一个被 GC 的水位线会让整个流程崩溃。
  2. 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_BASEHEAD 的祖先。
  • two-dot net diff 折叠掉「先加后撤」「中间抖动」,正好对应 recorder 只描述现状

6. 多批 commit 支持

/llmdoc:updateargument-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)

  1. $ARGUMENTS / 用户消息解析所有批次 flag。
  2. 每批用对应的 git diff --name-status 解析出 impact 路径集:
    • --range A..Bgit 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
  3. 最终变更集 = 所有批次路径集 ∪ 工作树(unstaged)∪ 暂存(staged)∪ 未跟踪,sort -u。重叠幂等。
  4. 噪声过滤:所有 diff/rev-list 都加 :(exclude)llmdoc/state/sync.md:(exclude).llmdoc-tmp/**,使每次运行重写状态文件不会自触发。
  5. 一次 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..HEADgit 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. 更新工作流步骤

  1. 加载 llmdoc skill;读 llmdoc/index.mdstartup.md、MUST 文档、相关 guides/memory/reflections/;校验任何 .llmdoc-tmp/investigations/ 报告是否对当前 revision 仍有效。
  2. 解析同步状态(新):能力探测(is-inside-work-treeis-shallow-repository、一次性取 H=HEAD);用锚定 awkllmdoc/state/sync.md 水位线;跑固定校验阶梯(先 cat-file -e、后 merge-base;HEAD-落后用双向 is-ancestor)。
  3. 计算变更集(新):解析批次 flag;每批用 git diff --name-status -M -C -z 解析(root 用空树、--commits 拒绝 merge);应用 :(exclude)llmdoc/state/sync.md 等 loop-breaker;与工作树+暂存+未跟踪并集;默认批次 RANGE_BASE..H
  4. 处理降级场景(非 git / shallow 低于边界 / 首跑 / 孤儿 / 分叉 / HEAD-落后),见 §10;降级运行绝不伪造水位线推进
  5. 按范围大小 × 作者归属 × 风险选模式;遵守硬地板与回填爆炸半径上限。
  6. 仅按模式需要 investigate,喂入已解析的 net-diff 路径清单;investigator 的 scratch 报告元数据记录解析出的 RANGE_BASE..H 范围。
  7. 仅当存在流程教训时 reflect;reflector 可读 per-commit 历史以捕获「为什么」。
  8. index.md Routing + must/doc-routing.md + reference/repo-surfaces.md 把变更集映射到受影响文档(rename 取 old+new 双路径);recorder 仅更新受影响文档、对批次 tip 状态记录、reconcile memory/doc-gaps.md
  9. 跑 active-memory 归档检查(不变llmdoc/state/memory/ 之外,不计入,无需改 stop.sh)。
  10. 同步 llmdoc/index.md;确保 llmdoc/state/sync.md 被列为知识 / Key Document。
  11. 推进水位线(新,recorder 拥有的终末步):仅在消费了已提交范围的完整成功更新后;若存在任何 in-progress 操作标记或 HEAD detached 则拒绝;按连续前缀规则推进到捕获的 H(默认)或最高未断前缀 tip(部分/批次);working-tree-only/失败/HEAD-落后绝不推进;只重写 watermark-commit + updated-at + watermark-subject
  12. 报告:模式、解析出的范围/批次 + N 个提交、旧→新水位线(或为何没动)、scratch/reflection 路径、归档动作、改动的稳定文档。

10. 边界情况表(已对本仓库验证)

情况 处理
非 git 仓库 is-inside-work-tree != true ⇒ 跳过所有 commit 逻辑,回退旧工作树+暂存+摘要检测,不读/不推进;报 "non-git: watermark inactive"。sync.md 可存在但休眠
shallow clone,水位线低于边界 is-shallow-repository=truecat-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_HEADCHERRY_PICK_HEADREVERT_HEADrebase-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 行使冲突最小;解决规则:取后代 SHAis-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.mdwatermark-commit=HEADrepo-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. 迁移与向后兼容

  1. 新 init going forwardcommands/init.md + skills/llmdoc-init/SKILL.md 在 bootstrap 时落 watermark-commit=HEADrepo-root,新仓库开局即同步,首次 /llmdoc:update 已有有效锚点。
  2. 已有 llmdoc 仓库升级(有 llmdoc/llmdoc/state/sync.md):首次 update 检测到文件缺失 ⇒ 走 first-run = 在当前 HEAD 落种子、不历史回填(前提:现有文档已反映到 HEAD,这正是「维护中的文档」的语义),本次只记工作树变更,再写水位线=HEAD。强制 ≥ analysis(假设未经证实),报告 "seeded baseline at HEAD ; documenting working tree only"。
  3. 可选回填:首跑加 --from <已知好提交>--since <tag>,diff base..HEAD(analysis/full,受爆炸半径上限),追溯吸收后写水位线=HEAD,报告 "backfilled from "。
  4. Dogfood:本仓库随引入提案的提交预置 sync.md 指向该提交,升级用户直接拿到显式水位线,而非依赖首跑派生。
  5. 原子性(鸡生蛋):水位线写入是普通的被跟踪编辑,与文档改动在同一个用户提交里(workflow 从不自动 commit)。它记录运行开始时捕获的 HEAD;下一次运行 旧HEAD..文档提交 全是 llmdoc/ 路径,:(exclude)llmdoc/** loop-breaker 快进、零文档工作。若用户漏改水位线就提交文档,下次靠同样的 llmdoc-only 快进自愈。
  6. 向后兼容: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.mdupdating-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. 待拍板的决策(含推荐默认)

  1. 升降级阈值(fast→analysis→full 的提交/文件数)。推荐默认:≤3 个自作者提交 ⇒ fast 可选;~4–15 或任一他人提交 ⇒ analysis;>15 或多批回填或重写恢复 ⇒ full。作可调指引,非硬闸。
  2. 回填爆炸半径确认阈值推荐默认:首跑/--since 超 ~20 提交或 ~50 文件时,处理前向用户显式确认。
  3. 是否为 llmdoc/state/sync.md.gitattributes merge driver(descendant-wins,else merge-base)。推荐默认:初期不配——单 SHA 行格式 + 文档化的「后代赢」规则对常见场景足够;并发分支冲突频发再加(merge driver 需每个 clone 注册 git config,.gitattributes 单独不保证生效,增加摩擦)。
  4. session-start hook 是否提示「文档落后 HEAD N 个提交」推荐默认留后续。若做,rev-list --countis-shallow-repository=false 守卫(否则报 "unavailable (shallow)")、排除 :(exclude)llmdoc/**、所有 git 调用 || true 包裹以免破坏会话。不属核心重构。
  5. 是否保留任何文件内审计日志推荐默认:不留——靠 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):
    1. 干净线性 W..HEAD
    2. 多批 --range A..B --range C..D
    3. --commits 含 root / 含 merge(应拒绝);
    4. rebase 后孤儿水位线(merge-base 恢复);
    5. git reset --hard 到旧提交(HEAD-落后,应拒绝);
    6. shallow clone 水位线越界;
    7. 非 git 目录(回退旧行为);
    8. 只动 llmdoc/ 的提交(应快进、零文档工作)。

本提案由多方案对比 + 对抗式边界审查综合而成;多处 git 行为已对本仓库历史实测。实现时建议先用 EnterPlanMode 把「改哪 22 个文件、什么顺序、怎么验证」定成可执行计划。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions