SyncEngine 技术协议
1. 协议目标
SyncEngine 协议把“从来源 GitHub 项目同步文档到 publish/**”约束为可实现、可审查、不会覆盖整理内容的三方 diff 过程。
它不是简单目录复制。它必须同时处理:
- 上游来源变化。
publish/**中人工或智能体整理后的变化。- 新增、更新、删除、保留和冲突。
- Sync PR 的可审查输出。
2. 覆盖产品特性
| 产品特性 | 覆盖方式 |
|---|---|
| 稳定文档发布分支 | 只读取配置声明的 ref,默认 docs-publish |
| 来源配置管理 | 从 config/sources.yaml 读取来源和目标映射 |
| 定时收集 / 手动收集 | 由 docs-sync.yml 调用 |
| 三方 diff 同步 | 使用 A/B/C 三方输入判断动作 |
| 同步冲突报告 | 输出 source-changes.json 和 PR summary |
| Draft PR | 同步结果只进入 Sync PR,不直接发布 |
| 项目映射目录 | 写入配置指定的 publishPath |
| 状态记录 | 更新 source lock 和 source changes |
3. 输入
| 输入 | 来源 | 说明 |
|---|---|---|
SourceConfig | config/sources.yaml | repo、ref、sourcePath、publishPath、include/exclude、deletePolicy、conflictPolicy |
| A:上次来源基线 | state/source-lock.json | 上次成功同步时每个来源文件的 hash、path、source ref |
| B:当前来源内容 | GitHub 来源仓库 | 当前 ref/sourcePath 下匹配 include/exclude 的文件 |
| C:当前工作目录 | publish/** | 当前控制仓库中的目标目录内容 |
3.1 SourceConfig 校验
同步前必须先校验全部来源配置。
配置约束:
- 每个
source.id必须唯一。 - 每个
publishPath必须唯一。 - 不同
publishPath不能互为父子目录。 publishPath必须位于publish/**下。sourcePath不能为空,不能指向仓库根目录,除非配置显式允许。- include/exclude 变更必须进入 Config PR。
违反约束时,SyncEngine 必须失败退出,不得执行部分同步。
3.2 配置变更影响
配置变更会影响 source-lock 的解释方式,不能被当作普通内容同步。
需要生成 config-impact-report 的变更:
| 配置变更 | 默认处理 |
|---|---|
ref 变化 | 重新读取来源,保留旧 lock,生成影响报告 |
sourcePath 变化 | 按潜在目录迁移处理,要求人工确认 |
publishPath 变化 | 不自动移动,要求人工确认迁移或重新接入 |
| include/exclude 变化 | 生成新增/排除影响报告 |
| deletePolicy 变化 | 只影响后续删除,不回放历史删除 |
约束:
- Config PR 必须展示受影响 source、旧配置、新配置和预估文件影响。
publishPath变化不能直接造成旧目录批量删除,除非 PR 明确声明迁移计划。- 配置变更合并后,首次 Sync PR 应标记为 config-impact sync,便于维护者重点审查。
4. 输出
| 输出 | 路径/载体 | 说明 |
|---|---|---|
| 同步后的文件变更 | publish/** | 仅对无冲突变更执行新增、更新、删除 |
| 新来源基线 | state/source-lock.json | 只记录成功 apply 的来源文件 hash 和 ref;冲突文件保留旧基线 |
| 同步变更报告 | state/source-changes.json | 记录 added、updated、deleted 、unchanged、conflict、delete_conflict |
| PR summary | Sync PR body / workflow summary | 给维护者审查同步影响 |
5. 状态结构
5.1 source-lock.json
{
"sources": {
"kunora-kgent": {
"repo": "innuama-coder/kunora-kgent",
"ref": "docs-publish",
"commit": "...",
"files": {
"docs/index.md": {
"sourceHash": "...",
"publishPath": "publish/system/components/kunora-kgent/index.md",
"lastSyncedAt": "..."
}
}
}
}
}
5.2 source-changes.json
{
"runId": "...",
"sourceId": "kunora-kgent",
"changes": [
{
"sourcePath": "docs/index.md",
"publishPath": "publish/system/components/kunora-kgent/index.md",
"action": "update",
"reason": "source_changed_publish_unchanged",
"baseHash": "...",
"sourceHash": "...",
"publishHash": "..."
}
],
"conflicts": []
}
6. 三方 diff 规则
| A:上次来源 | B:当前来源 | C:当前 publish | 动作 | 说明 |
|---|---|---|---|---|
| 不存在 | 存在 | 不存在 | add | 新增来源文件 |
| 不存在 | 存在 | 存在 | conflict | 目标路径已有内容,不能覆盖 |
| 存在 | 未变 | 未变 | unchanged | 无动作 |
| 存在 | 变更 | 未变 | update | 用 B 更新 C |
| 存在 | 未变 | 变更 | keep | 保留人工/智能体整理内容 |
| 存在 | 变更 | 变更 | conflict | 上游和 publish 同时修改 |
| 存在 | 删除 | 未变 | delete | 删除 C |
| 存在 | 删除 | 变更 | delete_conflict | 上游删除但 publish 已整理 |
判断“未变/变更”必须使用内容 hash,不使用文件时间。
6.1 source-lock 更新规则
source-lock.json 只能在同步动作成功 apply 后更新。
| 动作 | 是否更新 lock | 规则 |
|---|---|---|
add | 是 | 写入 B 的 sourceHash、sourcePath、publishPath |
update | 是 | 写入 B 的 sourceHash,C 已被更新为 B |
delete | 是 | 从 lock 移除该文件或记录 tombstone |
unchanged | 否 | 保持当前 lock |
keep | 否 | 来源未变,publish 已整理,保持当前 lock |
conflict | 否 | 保留旧 A,冲突报告记录 B hash |
delete_conflict | 否 | 保留旧 A,冲突报告记录 B 已删除 |
约束:
conflict和delete_conflict不能把 lock 推进到 B。- 冲突解决后,应由下一 次成功 apply 或人工标记解决时更新 lock。
source-changes.json必须保存冲突时的baseHash、sourceHash、publishHash,避免下一轮丢失 A/B/C 对比。
6.2 rename / move 策略
MVP 不做智能 rename/move 检测。
处理规则:
- 来源路径变化按
delete + add处理。 - 若旧 publish 文件相对 A 未变,按删除策略处理旧文件,并新增新文件。
- 若旧 publish 文件已整理,旧路径产生
delete_conflict,新路径产生add或conflict。 - 发布影响报告必须展示旧 URL 删除和新 URL 新增,避免审查者误判为普通更新。
- 后续如需保留 URL 或自动识别重命名,必须通过 ADR 扩展 SyncEngine 协议。
7. 删除策略
| deletePolicy | 行为 |
|---|---|
sync-if-unmodified | 只有 C 相对 A 未变时才删除 |
report-only | 不删除,只报告上游删除 |
never | 忽略上游删除 |
MVP 默认使用 sync-if-unmodified。
8. 冲突处理
冲突不自动解决。
冲突输出必须包含:
- sourceId。
- sourcePath。
- publishPath。
- baseHash。
- sourceHash。
- publishHash。
- conflictType。
- 建议处理方式。
Sync PR 必须标记冲突数量。存在冲突时,PR 不能被自动标记为可发布。
9. 安全和边界
- SyncEngine 只能写配置声明的
publishPath。 - SyncEngine 不允许写
docs/llm-wiki/**设计文档。 - SyncEngine 不允许直接 push
main。 - SyncEngine 不触发正式发布。
- SyncEngine 不调用 RAGFlow 做内容改写。
10. 验收用例
| 用例 | 预期 |
|---|---|
| 上游新增文件,目标不存在 | publish/** 新增文件,报告 add |
| 上游更新文件,publish 未改 | 自动更新,报告 update |
| 上游未改,publish 已整理 | 保留 publish,报告 keep |
| 上游和 publish 同时修改 | 不覆盖,报告 conflict |
| 上游删除,publish 未改 | 删除 publish 文件,报告 delete |
| 上游删除,publish 已整理 | 不删除,报告 delete_conflict |
| include/exclude 排除文件 | 不进入 B,不产生同步动作 |