矩阵不再在顶部叠报告:已完成 / 缺代码 / 已实现未跑 / 需授权四段分开, 把本轮 Wiki 撤销真实验收(12 项检查)、贡献级差异、维护面板写进对应行, 并如实记下封存链路缺口(CLI 无入口排空证明、API 封存未接线)、 死信无解除契约、Neo4j 场景与排空探针未跑。 新增正式 8088 待验收部署方案:迁移 067–072、loopback 配置差异、 缺配即拒绝的 fail-closed 规则、回滚与账号数据保留。只准备,未执行。
This commit is contained in:
+6
-4
@@ -37,11 +37,13 @@
|
||||
routing bindings,同一台机器上会封存"配置里那个库"而不是 API 的验收目标。现在环境变量
|
||||
优先、读不到才退回 bindings。
|
||||
|
||||
### Notes
|
||||
### Docs
|
||||
|
||||
- 本次真实验收(kgmaint 专用实例 + 原版 WeKnora API)与本快照之外的部署计划见
|
||||
`docs/library-acceptance-matrix.md` 与交付报告;矩阵按「已完成 / 缺代码 / 未跑 / 需授权」
|
||||
重排,不再在顶部叠报告。
|
||||
- `docs/library-acceptance-matrix.md` 按「已完成 / 缺代码 / 已实现未跑 / 需授权」
|
||||
四段重排(不再在顶部叠报告):补上本轮 Wiki 撤销真实验收、贡献级差异、维护面板;
|
||||
新增封存链路缺口(CLI 无入口排空证明、API 封存未接线)与死信契约缺口。
|
||||
- 新增 `docs/formal-8088-deployment-plan.md`:版本清单、迁移 067–072、运行配置、
|
||||
fail-closed 规则、回滚、正式账号与数据保留、部署风险。**只准备,未执行**。
|
||||
|
||||
## 开发快照(后台静默必须在关入口之后证明,且要能复核)
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
# 本机正式 8088 待验收部署方案(只准备,不执行)
|
||||
|
||||
> 本文是给父会话审查后用的一次性方案:**没有替换 8088,没有重启共享业务**。
|
||||
> 正式栈当前仍是旧版(0.1.114),不得对外声称新版已上线。
|
||||
|
||||
## 1. 版本清单
|
||||
|
||||
| 项 | 值 |
|
||||
| --- | --- |
|
||||
| 当前正式版本 | 开发版 `0.1.114` / 代码 `dc9e827`(api `knowledge-governance-api:library-dc9e8276560d`,web `knowledge-governance-web:source-dc9e8276560d`,worker `knowledge-governance-worker:library-dc9e8276560d`) |
|
||||
| 本次候选代码 | 分支 `feat/library-wiki-contribution-diff`,最终提交见交付报告(代码提交 + 文档提交分开列 SHA) |
|
||||
| 开发号 | 提交后由 `scripts/version.py` 自动得出(0.1.0 + 自 v0.1.0 起的提交数) |
|
||||
| 前端产物 | `npm --prefix web run build` → `web/dist`,由 `Dockerfile.web` 打进 web 镜像 |
|
||||
| 验收实例(已验证同 SHA) | `/tmp/kg-e2e/bin/{api,worker}` + 候选 `libraryctl`,`go version -m` 的 `vcs.modified=false` |
|
||||
|
||||
## 2. 数据库迁移
|
||||
|
||||
| 现状 | 目标 |
|
||||
| --- | --- |
|
||||
| 正式治理库 `schema_migrations.max(version)=66` | 需要依次应用 `000067` → `000072` |
|
||||
|
||||
```
|
||||
000067_library_release_authorizations 发布授权表
|
||||
000068_library_restore_kind 作业类型加 restore
|
||||
000069_library_write_target_mutex 写入目标互斥
|
||||
000070_library_maintenance 维护窗口表
|
||||
000071_library_maintenance_level 维护级别(library / instance)
|
||||
000072_library_instance_maintenance 实例级维护占用
|
||||
```
|
||||
|
||||
- 迁移在 api 启动时按 `AUTO_MIGRATE` 自动执行(与现有部署一致);**先备份治理库**再起新 api。
|
||||
- 回滚顺序:`072.down` → `071.down` → `070.down` → `069.down` → `068.down` → `067.down`,仅在新版 api 不再写入这些表之后执行。
|
||||
- 这些迁移只新增表/列,不改动既有知识治理数据;但 `000068` 扩了 `library_jobs.kind` 的取值。
|
||||
|
||||
## 3. 运行配置(正式栈)
|
||||
|
||||
正式 api 现有相关环境变量(`docker inspect knowledge-governance-p1-api-1`):
|
||||
|
||||
```
|
||||
PRODUCTION_RELEASE_REQUESTS_ENABLED=true
|
||||
PRODUCTION_BACKUP_RESTORE_VERIFIED=true
|
||||
PRODUCTION_WEKNORA_BASE_URL=https://weknora-test.dobot.cn
|
||||
PRODUCTION_RELEASE_AUTH_TOKEN_FILE=/run/release-secrets/production_release_auth_token
|
||||
PRODUCTION_WEKNORA_API_KEY_FILE=/run/release-secrets/target_weknora_api_key
|
||||
LIBRARY_AGENT_SOCKET=/run/library-agent/agent.sock
|
||||
LIBRARY_ARCHIVE_ROOT=/var/lib/knowledge-governance/library
|
||||
```
|
||||
|
||||
**没有** `LIBRARY_TARGET_*`(实例级维护目标)与 `LIBRARY_PRODUCTION_DATABASE_URL`。这必须保持"缺配即拒绝":
|
||||
|
||||
- 缺 `LIBRARY_TARGET_*` 时,worker 在 `prebackup` 阶段直接失败并写明
|
||||
「目标维护未接线,拒绝在挡不住入口/后台写入的情况下写目标」(`cmd/worker/library_handlers.go`)——**不会**退化成"库级锁假装维护"。
|
||||
- 缺 `LIBRARY_PRODUCTION_DATABASE_URL` 时 overview/差异按"未接入"显示,不显示 0 或被当成"无变化"。
|
||||
- `/api/v1/library/maintenance` 在实例未配置时返回 `instance` 缺省、`held=false`;页面显示「未接入 + 维护类操作 fail closed」。
|
||||
- `LIBRARY_AGENT_SOCKET` 指向的 agent 目前**没有运行**:`POST /api/v1/library/captures` 会 503 并写明"未接线",不会假装已封存。
|
||||
|
||||
新增配置只在需要真正开维护窗口时才加(本机验收实例的取值可作模板,见
|
||||
`evidence-maintenance/harness/candidate-maintenance.env`:`LIBRARY_TARGET_INSTANCE/PROJECT/WRITERS/NON_WRITERS/DOCKER_SOCKET/INGRESS_*`、`LIBRARY_TARGET_REDIS_*`)。
|
||||
|
||||
## 4. 部署步骤(父审后执行)
|
||||
|
||||
1. **冻结**:确认没有在跑的封存/同步/恢复作业(`GET /api/v1/library/jobs` 无 pending/running)。
|
||||
2. **备份治理库 + 归档根**:`pg_dump` 治理库;保持 `LIBRARY_ARCHIVE_ROOT` 卷原样(版本与 blob 都在里面)。
|
||||
3. **构建**:从候选 SHA 构建 api/worker/web 三个镜像,tag 里带完整 SHA;不在生产机上另行 build 源码。
|
||||
4. **切换**:`docker compose up -d api worker web`(只替换这三个容器;postgres/dingtalk-login/共享 WKNFull-* 不动)。
|
||||
5. **健康检查**:`GET /health`、`GET /api/v1/library/overview?scope=internal` 200、`GET /api/v1/library/maintenance?scope=internal` 200 且 `held=false`。
|
||||
6. **页面验收**(用户现有管理员会话,不再要候选密码):发布管理页 → 两库页签 → 目标维护面板显示"未接入/未占用"、Wiki 差异逐页行、作业进度与阶段。
|
||||
|
||||
## 5. 回滚
|
||||
|
||||
| 触发 | 动作 |
|
||||
| --- | --- |
|
||||
| api/web/worker 起不来或健康检查失败 | 三个容器回到 `*-dc9e8276560d` 镜像;迁移保持已应用(新表空表存在不影响旧代码) |
|
||||
| 新版写入后需要完全回到旧结构 | 先停新版 api/worker → 按 072→067 逆序 down → 恢复备份的治理库 → 起旧镜像 |
|
||||
| 归档不可读/版本异常 | 归档根只读回滚:保留新卷,切回旧卷挂载点 |
|
||||
|
||||
回滚**不触碰**正式账号、会话、治理库既有业务数据(迁移只加表/列),也不动 WeKnora 侧数据。
|
||||
|
||||
## 6. 正式账号与数据保留
|
||||
|
||||
- 管理员账号/会话在治理库 `users`/`sessions`(`kg_session` cookie)——迁移与镜像切换都不改这些表;不重置口令、不重新初始化。
|
||||
- 首页登录沿用现有会话;验收时不要用候选环境的账号。
|
||||
- 正式库与归档卷保留原路径,不做"清理/重建"。
|
||||
|
||||
## 7. 已知风险与前置条件(必须先解决或明确接受)
|
||||
|
||||
1. **`knowledge-governance-p1-release-worker-1` 处于重启循环**(日志每分钟 `connect release target database`,`RELEASE_WORKER_ENABLED=false`)。这是正式栈既有状态,不是本轮引入;部署前需要决定是修配置还是先停掉该服务,否则会持续刷日志。
|
||||
2. **封存能力未接线**(见矩阵"缺代码"):正式上线后页面能如实显示"未接入",但"封存测试版本"按钮仍不可用。如果验收要求现场封存,需要先部署 `libraryagent`(`cmd/libraryagent`,0600 unix socket)并把 `LIBRARY_AGENT_SOCKET` 指到它。
|
||||
3. **入口排空探针未在最终构建复现**(矩阵"已实现·未跑"):正式部署前若要拿这条证据,需要在生产机重复探针,并接受"闸门关得比客户端快"导致上传注入失败的可能。
|
||||
4. 部署本身就是生产变更:需要用户给的窗口与授权,本文不构成执行许可。
|
||||
@@ -1,80 +1,86 @@
|
||||
# 整库同步验收矩阵(真实证据对照)
|
||||
# 整库同步验收矩阵(真实证据对照,2026-10-29 重排)
|
||||
|
||||
## 入口级真实服务验收(2026-10-28,候选 API :18089,真实 PG/ES/MinIO,code_sha=ea4cffb)
|
||||
本文件是**唯一口径**:状态只写实际发生的事,四类严格分开,不用"部分通过"含糊过去。
|
||||
|
||||
| 项 | 状态 | 证据(`~/.hermes/handoffs/kg-candidate-e2e/`) |
|
||||
|---|---|---|
|
||||
| A0 首次发布(生产空基线) | 已实现 / 真实服务已验证 | `evidence/A0_baseline.json` |
|
||||
| A1 撤销(自洽派生快照) | 已实现 / 真实服务已验证 | `evidence/A1_revoke.json` |
|
||||
| B 按版本恢复(含未授权拒绝) | 已实现 / 真实服务已验证 | `evidence/B_restore.json` |
|
||||
| C 写阶段 SIGKILL 接管(复用同一预备份) | 已实现 / 真实服务已验证 | `evidence/C_kill_takeover.json` |
|
||||
| D 跨 kind 互斥 + 多 worker 单次执行 | 已实现 / 真实服务已验证 | `evidence/D_mutex_multiworker.json` |
|
||||
| E1 版本登记失败不报成功 | 已实现 / 真实服务已验证 | `evidence/E1_finalize_fail.json` |
|
||||
| E2 finalize 续作(只收尾) | 已实现 / 真实服务已验证 | `evidence/E2_finalize_resume.json` |
|
||||
| E3 预备份登记失败零写入 | 已实现 / 真实服务已验证 | `evidence/E3_prebackup_fail_zero_write.json` |
|
||||
| 远程生产写入/恢复/清理 | 未授权,未执行 | — |
|
||||
| Wiki 撤销贡献语义重算 | 未实现(仅页面级差异) | V03 |
|
||||
| 跨系统原子切点 | 未实现;按"导出后回读"协议,边界已写明 | — |
|
||||
| 口径 | 含义 |
|
||||
| --- | --- |
|
||||
| **已完成·已验证** | 有代码 + 真实服务(真实 PG/ES/对象存储/原版 WeKnora API)跑出来的证据,或既有 CI 检查覆盖 |
|
||||
| **缺代码** | 功能/契约本身没有实现;界面按"未接入"显示,不冒充 |
|
||||
| **已实现·未跑** | 代码与测试在位,缺真实环境执行证据(不是权限问题) |
|
||||
| **需授权/需环境** | 实现所需的动作属于生产写入/读取/恢复,或需要本会话拿不到的资源 |
|
||||
|
||||
对应 `docs/library-sync-acceptance.md` 的 30 个验收项。**状态只写实际发生的事**:
|
||||
A06(内容、问答、引用图片效果、性能报告)已由用户取消,不作为门槛,也不在本表。
|
||||
|
||||
- **已实现·已验证**:有代码 + 本机真实数据或真实归档/真实 PostgreSQL 跑过的证据,或既有 CI 检查覆盖。
|
||||
- **已实现·未验证**:代码与测试在位,但还缺真实业务环境的执行证据(例如需要隔离生产栈)。
|
||||
- **阻塞(需授权/环境)**:实现所需的动作属于生产写入、生产读取或生产恢复,当前授权明确禁止。
|
||||
- **未实现**:还没有代码,界面按"未接入/未知"显示,不冒充完成。
|
||||
---
|
||||
|
||||
A06(内容、问答、引用图片效果、性能报告)已被用户取消,本矩阵不含该项,也不再作为封版门槛。
|
||||
## 一、已完成·已验证
|
||||
|
||||
**状态口径(严格区分四件事)**:
|
||||
- 已实现·已验证:代码 + 本机真实归档/真实 PostgreSQL 或既有 CI 的实跑证据。
|
||||
- 已实现·隔离测试通过:代码 + 单元/集成测试(注入假生产目标),**没有**真实生产执行。
|
||||
- 已实现·未验证:代码在位但缺真实环境证据。
|
||||
- 未实现 / 真实执行未授权:前者是代码缺口,后者是**权限**缺口,两者不混用。
|
||||
| ID | 要点 | 承载位置 | 证据(本轮或前轮,均可回看) |
|
||||
| --- | --- | --- | --- |
|
||||
| V01 | 只有对内/对外两个目标,身份来自配置映射 | `internal/httpapi/library_*.go`;`web/src/ReleaseTargets.tsx` | overview 对非法 scope 返回 400;真实归档 internal/external 各自解析到唯一 KB |
|
||||
| V02 | 解析/索引/Wiki 就绪,验收绑定本版 | `v071/library_source_settlement.go` | 结清检查在封存前执行、缺项阻断;页面注明依据哪次快照 |
|
||||
| V04 | 完整固定版本:文档+处理结果+资源+索引+完整 Wiki | `internal/librarysnapshot/*` | 真实快照 f992d64e…:4 文档/38 分块/122 索引/21 资源/31 Wiki 页/55,309,044 字节,部件摘要逐项校验 |
|
||||
| V06 | 只同步知识数据,不含账号/会话/凭据 | 导出白名单(10 知识表 + 知识域索引 + 资源 + Wiki) | 部件清单无 users/sessions/api_keys |
|
||||
| B01 | 每次生产写入前完整预备份,失败零写入 | `v071.ProductionBackupStore.Prepare` + prebackup 阶段 | 真实 PG/ES 上预备份 3111e823…;故障注入后用它恢复并校验 |
|
||||
| B03 | 每版保存完整 Wiki 与引用关联并校验 | `library_snapshot` wiki 部件 + 摘要 | 真实快照 31 页逐页摘要;manifest 记录数量/字节 |
|
||||
| S01 | 数据库/资源/索引/Wiki 真实落地并校验 | `v071.ProductionLibraryTarget.Apply/Verify` | 本轮 sync `libsync-internal-…1793221776816380302`:database 67 行、resources 21、index 122、**wiki 63 行**全部 succeeded + verify succeeded;目标库 wiki 31 页 |
|
||||
| S02 | 生产不再解析/Embedding/生成 Wiki | index 阶段直接搬运归档 embedding | 真实 ES 写入归档 embedding,未调用模型 |
|
||||
| S04 | 各阶段失败/超时/重启可追溯可恢复 | `library_jobs`/`library_job_stages` + `ReconcileLibraryJobs` | 阶段与错误持久化;worker 清理"队列无对应作业"的孤儿 |
|
||||
| S05 | 幂等与互斥(多 worker 安全) | 幂等表 + active 唯一索引 | 真实 PG:同目标第二作业被拒;同键同 digest 复用;换 digest 冲突 |
|
||||
| R01 | 按指定版本手工恢复 | `POST /library/restores` + `library.restore` | 真实跑通:撤旧授权→503;签发 restore 授权→202→succeeded,内容回到目标版本(`evidence/B_restore.json`) |
|
||||
| R02 | 恢复失败显式异常、不回退测试库 | `Outcome.State=rollback_failed` + 独立阶段 | 隔离测试 + 前轮真半写场景(`partial.json`) |
|
||||
| P01 | 作业阶段与整体状态 | `library_jobs.state/stage`;页面阶段条 | 阶段勾选只认当前作业证据;无作业显示"尚未开始" |
|
||||
| P02 | 四类分项计数/字节/错误 | `stageSizesFrom(manifest)` + `library_job_stages` | 真实数据:database 67/88086B、resources 21/53490166B、index 122/1624880B、wiki 63/105912B |
|
||||
| P03 | 同步明细与异常 | 页面"同步明细"读阶段行 | 行来自持久阶段,刷新/重启可复现 |
|
||||
| U01 | 发布管理为主要入口(两库+单作业区) | `web/src/ReleaseTargets.tsx` | 按确认稿布局,父会话已在登录浏览器核验 |
|
||||
| U02 | 权限边界 | `canWrite`/`canCapture`,写操作需 engineer+ | 无权限不给封存入口;未接线显示通道状态 |
|
||||
| U03 | 操作确认 | 封存按钮 + 作业号 + notice | 同一作业号重放返回已存作业并提示 |
|
||||
| D01 | 保留策略(数量/天数) | `internal/libraryretention` | 未设置=0 按"全部保留",不编默认值 |
|
||||
| D02 | 占用测量 | `internal/librarysnapshot/usage.go` | 版本数/去重内容/blob/未引用/清单;两份真实清单 55,309,693 字节 |
|
||||
| D03 | 依赖保护 | `libraryretention.Build` 的 Protected/ProtectedBy | 真实归档两版均因"最近测试封存"受保护 |
|
||||
| D04 | 清理预览 | `Build` 的 Candidates/ReclaimPreviewBytes | keep=1 时仍 0 候选;回收预览只计独占字节 |
|
||||
| D05 | 容量门禁 | `CheckCapacity` + sync gate | 预算未设置→阻断;超预算→阻断并给预计字节 |
|
||||
| D06 | 测试残留口径 | 未引用 blob 统计 | 真实归档未引用 blob 为 0(口径,不等于扫过全部残留) |
|
||||
| **S03** | 实例级维护窗口:关入口→排空→静默证明→停写者→写入→校验→开入口 | `internal/libraryfreeze` + `librarymaintenance` + 迁移 070–072 | 前轮 7 场景真跑全绿(normal/kill/applyfail/partial/crossscope/recover_refuse/recover_ok),本轮 sync/drain 场景继续复用;`quiesce` 证据、`post_stop_*`、journal、gate drain.json 均可回看 |
|
||||
| **V03-a** | 撤销一份共享贡献后,保留另一份贡献、清除撤销方独有正文/引用/实体边、目录正确 | 由 WeKnora 源侧执行(`cleanupWikiOnKnowledgeDelete` + `EnqueueWikiRetract`),治理侧只比对/搬运 | **本轮真实验收**:kgmaint 隔离实例、原版 API 顺序入库两篇文档 → `entity/fa-bu-zha-men` 同页双来源(2097 字节)→ 真实 `DELETE /api/v1/knowledge/{id}` → 页面保留、来源只剩保留文档、A 独有正文(ZEBRA-A-7731/9910)清除、B 贡献仍在、引用 2→1 且只剩 B 的分块、目录不变、A 独有页面/摘要整页删除、索引页不再列出、实体边无死链。12/12 检查通过。证据:`kg-candidate-e2e/evidence-wiki-final/s8-final-checks-r1.json` + `06-wiki-*-r1.json` + `s3/s6-db-*.json` |
|
||||
| **V03-b** | 治理侧能把上述语义变化说清楚(不是页面级"无变化") | `internal/librarydiff/wiki.go` `CompareWiki`;`GET /library/overview` 的 `diff.wiki` | 逐页比对正文/来源/引用/出入边/目录,输出 `sources_removed`、`citations_before→after`、`changed`;单测 `wiki_test.go`、视图单测 `library_wiki_diff_test.go`;页面逐行展示(`wikiContributionLines` + 测试) |
|
||||
| **U04** | 维护级别/进度/恢复错误真实呈现 | `MaintenanceCard`(`web/src/ReleaseTargets.tsx`)、`/api/v1/library/maintenance` | 显示实例级/库级、持有 scope+作业、阶段、入口开/关、服务、开始/释放、进度(阶段名中文化)、恢复错误与处理记录;仅"窗口仍持有且作业失败"时给"按预备份恢复";无记录时"未接入 + fail closed"。组件测试 6 项 |
|
||||
|
||||
| ID | 验收要点 | 代码 / 承载位置 | 状态 | 证据或缺口 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| V01 | 只有对内/对外两个目标,身份来自配置映射 | `internal/httpapi/library_*.go`(scope 只接受 internal/external);`web/src/ReleaseTargets.tsx` | 已实现·已验证 | overview 对非法 scope 返回 400;真实归档里 internal/external 各自解析到唯一 KB(0c84a098… / 对外 KB) |
|
||||
| V02 | 全部解析/索引/Wiki 就绪,验收绑定本版 | `internal/releaseadapter/v071/library_source_settlement.go`;页面准备清单 | 已实现·未验证 | 结清检查(settlement)在封存前执行且缺项阻断;但对"实时测试库此刻就绪"仍需一次真实封存复核;页面已注明依据的是哪次快照 |
|
||||
| V03 | 删除文档后 Wiki 共享正文/实体/边/引用真实变化 | `wikiDiffOf`(页面级差异) | **未实现(架构阻塞)** | Wiki 页由 WeKnora 在**源侧**生成,治理侧只搬运快照;"两文档共贡献一页、撤一个另一个保留"必须在源侧重新生成才能验证。本轮未触发源侧生成(属源侧写操作,需原生 Wiki 接口)。页面级差异**不等于**语义重算,不当作完成 |
|
||||
| V04 | 完整固定版本:文档集合+处理结果+资源+索引+完整 Wiki | `internal/librarysnapshot/*`(10 表 + 索引 + 资源 + Wiki 部件);`artifacts` 清单 | 已实现·已验证 | 真实快照 f992d64e…:4 文档/38 分块/122 索引/21 资源/31 Wiki 页/55,309,044 字节;部件摘要逐项校验 |
|
||||
| V05 | 导出中源变更则拒绝或隔离 | `internal/libraryfreeze/*` + 预备份导出前后行数静默检查 | 部分实现·未验证 | 预备份导出前后行数不一致即失败(本机已验证会拒绝);真实并发写入的隔离演练仍未做 |
|
||||
| V06 | 只同步知识数据,不含账号/会话/凭据 | 导出白名单(10 张知识表 + 知识域索引 + 资源 + Wiki) | 已实现·已验证 | 部件清单里没有 users/sessions/api_keys;真实归档部件名可逐项核对 |
|
||||
| B01 | 每次生产写入前完整预备份,失败零写入 | `v071.ProductionBackupStore.Prepare` + `libraryrelease` prebackup 阶段 | 已实现·本机真实服务已验证 | 本机真实 PG/ES 上生成预备份 3111e823…(626 字节),带导出前后行数静默检查;故障注入后用它恢复并校验通过。生产目标本身未授权,未对真实生产执行 |
|
||||
| B02 | 资源在测试侧删除后仍可恢复 | 归档内容寻址 blob(`library_versions` + `librarysnapshot` 内容去重) | 已实现·未验证 | 归档不依赖测试临时对象;跨版本去重 55,309,693 字节;尚未做"测试侧删除后从归档恢复"的实测 |
|
||||
| B03 | 每版保存完整 Wiki 与引用关联并校验 | `library_snapshot` 的 wiki 部件 + 摘要 | 已实现·已验证 | 真实快照含 31 页 Wiki 与逐页摘要,manifest 记录数量/字节 |
|
||||
| S01 | 数据库/资源/索引/Wiki 真实落地并校验 | `v071.ProductionLibraryTarget.Apply/Verify`(真实 SQL + ES bulk) | 已实现·本机真实服务已验证 | 本机真实 PG/ES:导入 1 文档/1 分块/1 Wiki 页/1 索引文档并逐表身份摘要校验;写入只覆盖知识域表 |
|
||||
| S02 | 生产不再解析/Embedding/生成 Wiki | 索引阶段直接搬运归档原始文档与向量 | 已实现·本机真实服务已验证 | 真实 ES 写入的是归档里的 embedding 数组(未调用任何模型);生产环境本身仍未写入 |
|
||||
| S03 | 一致生效与失败补偿边界 | — | 未实现 | 跨 PG/ES/资源的一致切点与可见性保护未实现 |
|
||||
| S04 | 各阶段失败/超时/重启可追溯可恢复 | `library_jobs`/`library_job_stages` + `ReconcileLibraryJobs` | 已实现·已验证 | 阶段与错误持久化;worker 启动清理"队列已无对应作业"的孤儿并写明恢复责任,避免永久占锁 |
|
||||
| S05 | 幂等与互斥(多 worker 安全) | `library_jobs_idempotency`、`library_jobs_active` 唯一索引 | 已实现·已验证 | 真实 PostgreSQL 上验证:同目标第二个在飞作业被拒;同键同 digest 重放复用;同键换 digest 冲突 |
|
||||
| R01 | 按指定版本手工恢复 | `POST /library/restores` + `library.restore` 执行器 | 已实现·真实服务已验证 | 候选环境:撤掉全部旧授权→503;签发 restore 授权→202→succeeded,内容回到目标版本(`evidence/B_restore.json`,作业 `librestore-internal-20261028T120300.925270`)。远程生产未执行 |
|
||||
| R02 | 恢复失败显式异常、不回退测试库 | `Outcome.State=rollback_failed` + 独立阶段记录 | 已实现·隔离测试通过 | 测试断言恢复失败与同步失败可区分,且不触碰测试库 |
|
||||
| R03 | 存快/恢复快对比 | — | 未实现 | 未做测速;不写没有测量的数字 |
|
||||
| P01 | 作业阶段与整体状态 | `library_jobs.state/stage`;页面阶段条 | 已实现·已验证 | 阶段勾选只认当前作业证据;无作业时显示"本次作业尚未开始" |
|
||||
| P02 | 四类分项计数/字节/错误 | `stageSizesFrom(manifest)` + `library_job_stages` | 已实现·真实服务已验证 | 候选环境实测 database 67 行/88086 B、resources 21 行/53490166 B、index 122 行/1624880 B、wiki 63 行/105912 B;prebackup/verify/finalize/rollback 无可定义总量保持 NULL;证据 `evidence/A0_baseline.json` 的 `stages` |
|
||||
| P03 | 同步明细与异常 | 页面"同步明细"表读阶段行 | 已实现·已验证 | 行来自持久阶段,刷新/重启可复现;封存作业显示 export 阶段真实状态 |
|
||||
| U01 | 发布管理为主要入口(两库+单作业区) | `web/src/ReleaseTargets.tsx` | 已实现·已验证 | 按确认稿布局;父会话已在登录浏览器核验两库切换与抽屉 |
|
||||
| U02 | 权限边界 | `canWrite`/`canCapture`,POST 需 engineer+ | 已实现·已验证 | 无权限时不给封存入口;未接线时说明通道状态 |
|
||||
| U03 | 操作确认 | 封存按钮 + 作业号 + notice | 已实现·已验证 | 同一作业号重放返回已存作业并明确提示 |
|
||||
| D01 | 保留策略(数量/天数) | `internal/libraryretention`;`LIBRARY_RETENTION_VERSIONS/DAYS` | 已实现·已验证(单测+真实归档) | 未设置=0 时按"全部保留",不编默认值;设置了才生成候选 |
|
||||
| D02 | 占用测量 | `internal/librarysnapshot/usage.go`;系统设置→存储管理 | 已实现·已验证 | 版本数/跨版本去重内容/blob 表观与分配/未引用/清单;真实两份清单 55,309,693 字节 |
|
||||
| D03 | 依赖保护 | `libraryretention.Build` 的 Protected/ProtectedBy | 已实现·已验证 | 真实归档上 2 个版本都因"最近测试封存"受保护;受保护版本永不进入候选 |
|
||||
| D04 | 清理预览 | `libraryretention.Build` 的 Candidates/ReclaimPreviewBytes;`GET /library/storage` | 已实现·已验证 | 真实归档:策略 keep=1 时仍 0 候选(两版均受保护);回收预览只计独占字节 |
|
||||
| D05 | 容量门禁 | `libraryretention.CheckCapacity` + sync gate | 已实现·已验证(单测+真实数据) | 预算未设置→阻断并说明;超预算→阻断并给出预计字节 |
|
||||
| D06 | 测试残留 | 归档未引用 blob 统计(UnreferencedBlobs/Bytes) | 已实现·已验证 | 真实归档未引用 blob 为 0;这只是口径,不等于扫描过所有测试残留 |
|
||||
| D07 | 实际回收 | — | 阻塞(需清理授权) | 自动清理默认关闭,不提供删除动作 |
|
||||
## 二、缺代码
|
||||
|
||||
## 差异比对(本轮新增)
|
||||
| ID | 缺口 | 现状与影响 |
|
||||
| --- | --- | --- |
|
||||
| R03 | 存快/恢复快对比 | 没有测速实现,也未测。不写没有测量的数字 |
|
||||
| 死信解除契约 | 死信只能靠人工清理,没有"解除/豁免"契约 | WeKnora `task_dead_letters` 无解决标记;导出/封存按"存在即阻断"(`library_source_settlement.go`)。本轮真实验证:早期错误模型配置产生的 2 条 wiki 死信直接阻断整实例结算,清理方式只能是删除行(记录:`deadletters-before-delete.json`,sha256 `dd4d8f08…`)。要变成可运维契约,需要上游给出解决标记或治理侧设计"带证据的解除"——**未实现** |
|
||||
| 封存入口接线(本机) | `POST /library/captures` 需要 `LIBRARY_AGENT_SOCKET` 指向真实 agent | 候选/正式 api 都配置了 socket 路径,但本机没有运行 `libraryagent`,接口会 503 并明示"未接线"(不是静默失败) |
|
||||
| CLI 实例级封存 | `libraryctl capture` 只建库级窗口,不关入口 | 真跑结果:WeKnora 收到 SIGTERM 固定 `exit=1`,没有入口排空证明时被拒(`未优雅退出(exit=1)`)。要在"不停共享栈"的前提下封存真实撤销后的库,需要把实例级 + 闸门接进 CLI(或部署 agent)——**未实现** |
|
||||
|
||||
`internal/librarydiff` 以**稳定文档 ID** 为身份、以排除 `updated_at`/`updated_by` 后的整行摘要为内容,
|
||||
比较两份归档快照的 `knowledges` 行数组,输出新增/更新/撤销。真实归档上验证:
|
||||
对内快照 4 篇对空集为"新增 4",反向为"撤销 4",缺失快照返回原因而不是空差异。
|
||||
生产权威版本建立后即可直接给出生产→测试差异;在生产版本未知时页面保持"未接入"。
|
||||
Wiki 页面级差异单独返回(`diff.wiki`),明确标注"不代表实体/边/引用重算"。
|
||||
## 三、已实现·未跑
|
||||
|
||||
## 未通过项如何阻断
|
||||
| ID | 要点 | 为什么未跑 |
|
||||
| --- | --- | --- |
|
||||
| V05 | 导出中源变更则拒绝或隔离 | 预备份导出前后行数静默检查已有(本机验证会拒绝);真实并发写入的隔离演练未做 |
|
||||
| B02 | 资源在测试侧删除后仍可恢复 | 归档内容寻址已实现;"删测试侧对象再从归档恢复"实测未做 |
|
||||
| S03-b | 死信/孤儿任务阻断的负向演练(本轮只验证了真实死信阻断) | 真实阻断已发生并记录;系统化的负向矩阵未跑 |
|
||||
| **V03-c** | **带"撤销后共享页"的整库封存 → 同步 → 恢复** | 上游语义已验证、治理差异已实现;但这台机器上 CLI 封存止步于上面的 exit=1 缺口,API 封存未接线,所以"封存一份含撤销结果的 Wiki 版本,再同步/恢复并逐行比对"**没有产出真实证据**。旧证据 `drain-task-new-c`(03:39)属于 0ef7984 之前的构建,不能拿来证明当前 SHA |
|
||||
| **Neo4j 启用场景** | 代码在 `library_export.go` 检查 `graph_enabled`,启用即 `Neo4j graph snapshot is not supported` 拒绝 | 本轮没有真跑"启用 Neo4j 的库被拒":kgmaint 实例 `NEO4J_ENABLE=false`、测试库 `graph_enabled=false`。代码路径在,真实执行未做 |
|
||||
| **排空竞态探针(最终构建)** | 关入口→排空期间产生的真实任务必须等到队列归零才停写者 | 本轮在最终构建(1faf513)上重跑 6 次(`drain-task-final-1faf513{,-r2,-r3,-r4,-r5,-r6}.json`),上传请求全部 `Broken pipe`、队列深度始终 0:闸门在 202 之后 <0.2s 就关监听,客户端来不及把请求送进去。**未复现**,不记为通过;旧 `drain-task-new-c` 的时间线仍是旧构建证据 |
|
||||
| U05 | 正式 8088 页面验收 | 正式栈仍是 0.1.114,本轮未替换;维护面板/逐页 Wiki 差异只在测试与候选构建里验证过 |
|
||||
|
||||
- `POST /api/v1/library/syncs`:闸口关闭、缺恢复演练证据、生产端点未配置、生产权威版本未知、
|
||||
目标快照无预备份——任一成立即 503 并逐条列原因,**不创建作业**。
|
||||
- 页面"启动同步"按同一 gate 禁用,title 与卡片列出全部缺项;不存在"点了才失败"的假按钮。
|
||||
- 封存缺项(结清检查不通过、契约不符、导出校验失败)一律阻断封版,不改状态蒙混。
|
||||
## 四、需授权 / 需环境
|
||||
|
||||
| ID | 事项 | 缺什么 |
|
||||
| --- | --- | --- |
|
||||
| D07 | 实际回收(真删对象/版本) | 自动清理默认关闭;删除属于真实清理,需单独授权 |
|
||||
| 远程生产 | 生产写入/恢复/清理 | 用户授权范围只到本机专用验收实例;远程生产一律未执行 |
|
||||
| 生产目标维护 | 对共享业务栈开维护窗口 | 本轮明确不停 `WKNFull-app`;维护只在 kgmaint 专用实例上跑 |
|
||||
| 生产封存/同步真实链路 | 需要在生产机部署 `libraryagent` 并接线 | 需要用户批准的部署窗口与凭据录入 |
|
||||
|
||||
---
|
||||
|
||||
## 五、本轮新增结论(写在这里而不是报告顶部)
|
||||
|
||||
1. **Wiki 撤销语义是上游行为,必须用上游 API 验**:顺序入库才产生"两篇共贡献一页"(并行入库会各自抽到不同 slug 生成两页,这是上游已知行为)。撤销后页面保留、贡献按文档摘除,12 项检查全部通过。
|
||||
2. **治理侧不能只报页面级差异**:撤销共享贡献时页面仍是"更新",页面级计数会显示成"无变化"。已改为逐页语义比对,能指出"来源 -1、引用 2→1、正文变了、出边少了一条"。
|
||||
3. **封存链路是本机当前的真缺口**:CLI 封存被 `exit=1` + 无入口排空证明挡住,API 封存没有 agent。这不是"Wiki 语义没验",而是"带撤销结果的版本没法在本机封出来"。正式部署必须先接线 agent(见 `docs/formal-8088-deployment-plan.md`)。
|
||||
4. **死信不是可忽略的告警**:一条失败的 wiki op 就足以让整实例结不了算。要么上游给解决标记,要么治理侧设计带证据的解除,当前两者都没有。
|
||||
Reference in New Issue
Block a user