Files
knowledge-governance/docs/USER_FLOWS_ACCEPTANCE.md
T

36 KiB
Raw Blame History

用户操作流程与验收清单

按流程逐项验收用。每条给出:入口 → 用户步骤 → 期望 → 验收证据 → 状态。 状态标记:✅ 已实测(附证据)|⏳ 待验收|⚠️ 已知限制。 深度细节见各自专题文档(P3…/P5…/P6…/P8…/PRODUCTION_BACKUP_RECOVERY.md/WIKI_CO_TRANSPORT…)。

统一入口:http://127.0.0.1:8088/(测试栈;右侧面板可直接操作)。角色:管理员 / 文档工程师 / 只读。


A. 内容进入

A1 取件(钉钉知识库 / PP 清单)

  • 入口:来源与目录 → 内容来源(勾选条目;钉钉扫码会话失效时先刷新会话——注意:走 dws 取件后钉钉这条不再依赖浏览器会话)
  • 步骤:选来源 → 展开目录 → 勾选 → 取件(后台入队)
  • 期望:解析监控出现"处理中";失败进死信,可重排
  • 证据:解析监控 → 死信 队列;重排轮次键 artifact-fetch:<version>:r<n>
  • 状态:✅(死信重排修复 cddc4c9)
  • 2026-10-10 端到端实测(界面操作):勾选 App账号注册.adoc → 标记"对内" → 取件
    • 首次取件终态失败 artifact_fetch_rejected: credentials(1/8 次即终态)——已定位并修复: artifactfetch 的 runner 给子进程的是白名单环境(防凭据外泄),但没透传 ARTIFACT_DINGTALK_FETCHER/DWS_BIN/DWS_CONFIG_DIR/DWS_HOME/DWS_DISABLE_KEYCHAIN, dws 找不到配置目录与文件型 keychain → 报"未登录" → 被映射成 credentials 终态 (且按设计不回落老路径)→ 钉钉取件在真实链路上整体失败。脚本单跑正常,因为绕过了白名单 (提交 d29e04e;同时失败时把 stderr 尾部脱敏后带进错误,否则只能看到裸 code)
    • 重排(重新入队)后:artifact.fetch 1 次完成 → 产物 sn-753f….docx 4799 B、 file 认作 Microsoft Word 2007+、zip 12 项;content_objects 记 validated_format=docx、 content_state=verified、sha256=5d8f38d0… ✓
    • 教训:dws 取件的端到端证据必须在"平台真实调用路径"里取(脚本直接跑会漏掉环境白名单这类差异)
  • 第二遍(2026-10-10,另一条路由):技术文档/人形适配大寰电爪相关资料/DH-AG95夹爪适配-指令协议.md(3513 B,drive 直下,不走 adoc/sheet)
    • 路由回执:"策略已保存 1 份;已记入意图 1 份;排队从源头取件 1 份;列入待入库 1 份"
    • artifact.fetch 一次成功;spool sn-22da9a36….md 3513 B、UTF-8、正文以 # DH-AG95夹爪适配-指令协议 开头 ✓
    • 易误判点:content_objects 按 sha256 内容级去重,命中已有行时只更新 updated_at/不更新 created_at—— 本次复用了 2026-09-14 创建的 co-e90af1d6…(sha 55fae4b4…、3513 B、md)。核对时必须走 source_item_versions.content_object_id → content_objects,不要按 created_at 过滤列表(我一开始就这么看漏了)

A1 补充:钉钉取件改走 dws(f4e1ecc / 6496ee3 / 9155bd3,2026-10-09)

  • 开关:ARTIFACT_DINGTALK_FETCHER=dws|auto|legacy,线上为 auto(先 dws;仅可重试错误回落老路径)。
  • 路由:adoc→doc export docx;axls→sheet 读取转 xlsx;其余→drive 直下;dlink/hlink→missing;amind/appt→unsupported(详见 docs/DINGTALK_DWS_FETCHER.md)。
  • 平台侧判定全部保留:后缀路由、PPT 必须 PDF 双胞胎、magic、大小上限、sha256、spool 原子写、空壳检查 → dws 只负责"取字节"。
  • 本人复核(2026-10-09,在已部署 worker 内):
    • 接线:dws v1.0.63(Edition: open)在 /usr/local/bin/dws;DWS_HOME=/data/home、DWS_CONFIG_DIR=/data/dws、DWS_DISABLE_KEYCHAIN=1;宿主两份 state 已按文档挂载 ✓
    • 鉴权(必须带 HOME=DWS_HOME,直接 docker exec dws 会误报"未登录"):authenticated:true、refresh_token_valid:true、refresh_expires_at 2026-11-08、账号 姚章浩 / 深圳市越疆科技股份有限公司 ✓
    • 真实调用:dws wiki +space-list --limit 1 → ok:true,返回"公司级技术支持知识库" ✓
    • 回归测试:artifact_fetch_test.py 18 项、test_dws_error_map.py 19 断言全绿 ✓
  • 运维依赖(验收时作为背景知识):
    • refresh token 30 天轮换:到期前 7 天按 runbook dws auth login --device 续期(本人浏览器授权);出现 code=credentials 立即停批,不要重试风暴,授权后 --retry-failed 续跑。
    • 在线文档并发建议 ≤4(当前 ingestion_concurrency=2 ✓ 满足);8 路混合时巨型文档曾现 export_failed(已判可重试)。
    • 回退开关:ARTIFACT_DINGTALK_FETCHER=legacy 可完全绕过 dws。
    • CI:Gitea Run #6/#7/#8 全 success(#8 = 含本清单的 b1b4ca5)✓

A2 标注与目录范围(路由到哪个库)

  • 入口:来源与目录 → 勾选 → 标记:对内 / 对外 / 不需要(目录级标记决定整目录)
  • 步骤:标注 → 生成修订
  • 期望:标记按 系统设置 → 测试知识库绑定 分发到对应测试库;不需要 不入库
  • 证据:GET /api/v1/source-catalog/routing-capabilities;绑定未配时标记被拒
  • 状态:✅ 本期实测(界面勾选 → "对内" → 目录刷新完成;internal_enabled/external_enabled 均 true)

A3 生成修订 / 新版本(发布准备)

  • 入口:发布管理 → 生成新版本 → 批准 / 驳回
  • 期望:只有批准后的变更集才能进入密封与批次
  • 证据:变更集版本号与审核动作记录
  • 状态:✅(见 P5_REVIEW_AND_SEAL_ACCEPTANCE.md)
  • ⚠️ 顺序约束(2026-10-10 实测踩到):生成新版本 是生成即固化的——它把当时的 "测试是否就绪 / 是否批准"快照写进条目。若在处理完成/审核批准之前生成,则该条目会被 记为 not_ready,且此后 确认冻结 不会重算(冻结沿用生成时的条目), 重生成才是唯一的刷新方式。正确顺序:处理完成 → 批准 → 生成新版本 → 确认冻结 → 建批次。 实测:批准前生成 v2(新增1/阻断195,本条 not_ready)→ 批准后重生成 v3(新增2/阻断194, 本条 ready/can_release=t)→ 冻结 → 定批次 ✓

B. 入库与监控

B1 待入库 → 开始处理 → 完成

  • 入口:文档工作台(待入库列表)→ 选中 → 开始处理
  • 期望:上传 → 解析 → 索引 → completed;目标库里能看到知识条目与 wiki 页
  • 证据:单文档 E2E tp-3056261d46396a0695497f38421346b3 → processing_state=completed;知识 cf0af40f-7c0c-4526-ad6a-4515b3b2ca00(4 chunks,wiki 9 页)
  • 证据(2026-10-10 界面实测):解析监控 → 待入库(1) → 开始处理 → tp-c3412382b66f55404d2b51672ffd0549 → parse_status=completed、ingest_state=uploaded、poll_sequence=4、last_remote_status=completed; 知识 98be235b-68cf-43e1-94ca-419f5b97fddb;任务链 test.upload → test.poll×4 → test.validate 全 completed ✓
  • 证据(2026-10-10 第二遍):tp-466164a074cb22efb → processing_state=completed(polls=8、remote=completed、 测试库 0c84a098…);任务链 test.upload → test.poll×8 → test.validate 全 completed ✓
  • 证据(2026-10-10 第三遍,PDF/MinerU):tp-4aad3a6663efe94ccc76b96… → processing_state=completed(polls=6、remote=completed); 审核页给出「知识读取:通过;内容处理:通过;搜索验证:需人工确认」✓ → PDF/MinerU 解析链路通
  • 状态:✅ 2026-10-08 / 2026-10-10 实测(三份小文档:docx 转换路 + md 直下路 + pdf/MinerU 解析路)

B2 解析监控与补救

  • 入口:解析监控 → 处理中 / 需要你出手 / 需下架 / 死信 / 已完成 / 知识库差异 / 知识库解析进度
  • 步骤:看"需要你出手" → 重排失败项 / 处理需下架项
  • 期望:失败可重排;"需下架"列出仍留在库里的禁止项
  • 证据:死信与重排链路(cddc4c9)
  • 状态:✅ 链路|⚠️ "重排"按钮行为目前只有单测覆盖(页面当时无失败项可点)
  • 2026-10-10 界面实测(第三遍:禁止导入 → 需下架):把已入库的 DH-AG95夹爪适配-指令协议.md 改成「禁止导入」 → 回执「策略已保存 1 份、标记禁止 1 份、取件 0、待入库 0」✓,目录表格变为「禁止导入」✓
    • 发现真问题(已修):需下架 车道恒为 0。车道 SQL 判 retrieval_state='enabled',但 retrieval_state 在这套状态机里描述的是是否发布到生产(unpublished/disabling/disabled/failed),全仓没有任何代码写过 'enabled' —— 实测线上 5/5 发布单元都是 unpublished,界面上「仍在知识库里的文件会出现在这里」的承诺不成立。 修为与设计注释一致的口径:仍在测试库存活(completed + 有 knowledge_id + 未 disabling/disabled)+ 来源项 policy_state='blocked'; 线上库对照:旧判据 0 → 新判据 1,命中正是那支被禁止导入的 .md ✓;页面随即显示 需下架(1)· 对内知识库 · 1 份仍在检索中 ✓
    • 仍未闭环(待排期):① 车道只按库计数,没有条目级列表;② 移除动作(test.remove 工作流,已实现且 worker 已注册) 目前只由变更集删除路径触发,页面没有入口;③ 该工作流是 DeleteKnowledge(从测试库删知识条目,重新标注可再入库), 与旧文案"停用检索(可恢复)"的措辞不同 —— 文案已按事实改写(WeKnoraMonitor.tsx / SourceCatalogBrowser.tsx)

C. 发布与回滚

C1 审核 → 密封 → 建批次 → 锁定

  • 入口:发布管理 → 创建草稿批次 → 密封 → 锁定
  • 期望:密封后批次不可改;锁定后才能授权生产发布
  • 证据:批次 rb-9678722c…(1 项);变更集 rcs-832f0cf9… v1(196 项:1 可发布 / 195 阻塞)
  • 证据(2026-10-10 界面实测):批准 → 生成新版本(v3 rcs-69c1810b…,新增2/阻断194)→ 确认冻结 (state=confirmed、confirmed_by=admin)→ 勾选本条 + 批次名 验收-App账号注册-20261010 → 创建草稿批次 → 锁定清单 → 批次 rb-d6eb1c91ee49ab3aedaa16d3c3c91f85(sealed、1 项、 绑定 v3、review_id=rv-e4b38040…、action=upsert、decided_visibility=internal)✓
  • 证据(2026-10-10 第二遍,.md):批准 .md → 生成新版本(v4 rcs-2b22276e…,新增3/阻断193)→ 确认冻结 → 批次 rb-f1819fb0c4196ae6e22e600874954a0c(验收-DH-AG95指令协议-20261010,sealed、1 项、 仅 tp-466164a074cb22efb、绑定 v4)→ 锁定清单 ✓;顺序规则再次验证:批准前生成的 v3 里该条仍 not_ready, 批准后重生成 v4 才 ready/can_release=t ✓
  • 证据(2026-10-10 第三遍,PDF):v5 rcs-8869e6f0…(新增4/阻断192,含 相机线束固定孔位尺寸图.pdf ready) → 批次 rb-9dfcff203f5fc5e0b4063d3d8b09410b(验收-相机线束孔位图-20261010,sealed、1 项)→ 锁定清单 ✓
  • 状态:✅ 2026-10-08 / 2026-10-10 实测(三份文档各一次)

C2 授权发布(双闸 + 页面确认语句)

  • 入口:发布管理 → 授权发布 → 逐字输入确认语句 publish-to-production → 确认执行
  • 期望:执行 completed + verified;目标库出现该文档;不出现"一键发布"
  • 证据:执行 rx-dd47920a5b5f5019af4f197fd0c2f92c;目标库 0b217e8a… 文档数 2069→2070;两个闸门 PRODUCTION_RELEASE_REQUESTS_ENABLED / PRODUCTION_BACKUP_RESTORE_VERIFIED 均为 true
  • 状态:✅ 本期实测(页面通道由管理员会话记录操作人;另支持 0600 Bearer 通道)
  • 2026-10-10 界面实测(含首个真实跨环境 wiki 发布):授权前留档 2m52s → guardcheck 两库 PASS → 输入 publish-to-production → 确认执行
    • 首次执行失败:项级 release_verification_failed,worker 日志 release wiki replace scope in_scope=0 foreign=8 foreign_ref_stripped=0 untouched=1 skipped_library=1 degraded=true → 已定位并修复:写入侧(replaceWiki)按设计保留目标库外来源 wiki 页, 而校验侧 VerifyWiki 仍要求"生产页集合与包内快照完全相等" → 跨来源共库必然误判。 修复:校验改为与写入侧同一套归属口径(共用 packageWikiOwners)—— "快照内页必须都存在 + 多出来的页只有在不属于本包时才放行",本包拥有却残留/缺失仍判失败, 撤权仍禁止任何页引用被撤权文档(提交 eabe55b)
    • 重排后完成:执行 rx-63b815604766c750f95c6bba8682a4fe → completed(1/1、failed 0、 项级 verified、production_revision=rv-e4b38040…、bundle_sha256=19a85aea…), worker 日志 in_scope=1 foreign=8 … degraded=true 无失败行
    • 页面原文:该执行 发布 已完成 1/1;每项结果 App账号注册.adoc → 发布并验证通过 · 目标库 0b217e8a… · 生产可用 ✓
    • 第二遍(2026-10-10,.md)修复后首次即成功:留档 20261010T023538Z-0b217e8a (PG knowledges 3830→3831、wiki_pages 1982→1983,与上一轮发布/回滚后的库状态吻合)→ guardcheck 两库 PASS → 执行 rx-bc714172a3b0c77885ef18a30dd3ac6b → completed 首轮通过(1/1、failed 0、项级 verified), worker 日志同样 in_scope=1 foreign=8 … degraded=true 且无失败行 → 页面 发布 已完成 · 1/1、每项结果 DH-AG95夹爪适配-指令协议.md → 发布并验证通过 · 生产可用 ✓
    • 第三遍(2026-10-10,PDF):留档 20261010T030026Z-0b217e8a(PG knowledges 3831→3832、wiki_pages 1983→1990) → guardcheck 两库 PASS → 执行 rx-7044688b5b9432c1cb04f8cf251c3679 → 首轮 completed (1/1、failed 0、项级 verified、production_revision=rv-aac1a725345c7dbf56c03bde1f5d5442); worker 日志 in_scope=8 foreign=8 … degraded=true 无失败行(这次包内 wiki 8 页,归属门禁在更大页集上同样正确)✓
    • ⚠️ 第三遍踩到真问题(已修,见 F.3g):授权发布被回绝 production release requests are disabled —— 因为我用 deploy-api-web.sh 重建 api 时没叠 compose.release-gates.yaml,两道生产门禁被静默关掉; 补上门禁文件重新 up 后重试即成功 ✓(页面当时只在点击处回一句英文错误,很容易误判成功能坏了)

C2b 下架(delete 动作)★2026-10-10 首次实测

  • 入口:发布管理 → 发布计划 里对「删除」行勾选 「选择删除…」 → 勾选 「确认删除生产知识」(二次确认框) → 填批次名 → 创建草稿批次 → 锁定 → 留档 → 授权发布(同一固定语句 publish-to-production)
  • 期望:执行项 action=delete、production_revision=absent、state=verified;目标库该知识停用(行保留,可恢复)
  • 证据(2026-10-10):先把 PDF 重新发布使生产侧"存在"(rx-80ad81d5…,目标行 enable_status=enabled)→ 在来源目录把它改为禁止导入 → 计划给出 相机线束固定孔位尺寸图.pdf → action=delete / production_state=present / can_release=true ✓ → v8 rcs-8702357179…(delete_count=1、113 项)→ 批次 rb-8c1ad1c9ba849c7cfe3a5045b53eb880 (验收-下架孔位图-20261010、sealed、1 项、action=delete、绑定 v8)→ 留档 20261010T033100Z-0b217e8a + 门禁 PASS → 执行 rx-6c19232a4ce1a19f937292a0a60b6c84 → completed(1/1、failed 0、项级 verified/delete、 production_revision=absent);worker 日志 in_scope=11 foreign=8 … degraded=true ✓ → 独立核对目标库:相机线束固定孔位尺寸图.pdf.pdf 行仍在、parse_status=completed、 enable_status=disabled ✓(下架=停用检索、不删行,可恢复)
  • ⚠️ 过程中踩到并修掉 Bug #6(见 F.3h):禁止导入只写 source_items.policy_state,而两处"期望策略"解析 (计划 + 建批 delete 就绪判定)都不看它 → 计划不给 delete、建批直接 422

C9 数据源配置:列出来 → 勾选 → 变成来源 ★2026-10-10 面板实测

  • ① 来源卡片不再做文件格式筛选(格式由「来源与目录」的目录级标注决定,就近覆盖上级)。
  • ②③ 「可加入的来源」按当前钉钉账号列出知识库与钉盘空间: 知识库 18(组织 17 + 我的知识库 1)、钉盘 7(我的文件 + 6 个组织空间); 已登记的三条钉钉来源自动标「已是来源」并禁用勾选。 列不出来时给原因而不是空白:dws 未登录/权限不足 → 503 + 提示;钉盘被拒时只丢钉盘那一栏 (drive_notice),知识库照常显示;被上限截断时提示"可能不全"。
  • ④ 勾选 → 加入为来源(选中 N) → 走既有登记路径,首次目录扫描自动排队。 实测:勾选「视觉文档」→ 新来源 sc-8a4be5b8293d1c9a048c3c7e9b194155(dingtalk、enabled)
    • source.catalog.refresh 已完成。扫码会话失效时会直接弹二维码,扫完自动重试本次登记。
  • 后端:GET /api/v1/dingtalk-spaces/available(管理员、只读,dws)。api 与 worker 共用同一份 dws 状态(bind mount 同一份:refresh token 轮换制,不能各留一份)。

C8 一键发布(建批+锁定+授权合成一个动作)★2026-10-10 实测

  • 入口:审核行上的 发布到生产(管理员 + 生产门禁开启时显示),或工具栏「发布到生产(选中 N 项)」。
  • 证据:点行级按钮 → 确认弹窗输入固定语句 → 提交 → 库内一次得到: 批次 rb-bee80550b6fae758a4b1076db1c6bc36(立即发布 · 2026/10/10、sealed、1 项、绑定冻结版本)
    • 执行 rx-3d831129e01322a8dfcf08b09cd4adbe(publish、completed、项级 verified、production_revision=rv-934f2a64…) → 随后在页面上回滚:rx-43835fac7d218c008933f1cb93c75b2b → rolled_back、项级 production_revision=absent (回滚状态由适配层读回校验 verify restored 后才置位)。
  • 与之前对照:同一条文档从"已批准"到"发出",此前需 找条目→勾选→填名→建批→滚到清单→找批次→授权→输语句→提交(~9 次交互); 现在 3 次(点发布 → 输固定语句 → 提交)。
  • 约束未放松(服务端测试固化):请求门禁关闭 → 403;备份恢复验证未过 → 412;非管理员 → 401;错语句/空选择 → 400; 建批失败(门禁不通过)→ 422;密封后不满足生产门禁 → 409。

C7 批量审核 + 建批即锁定 ★2026-10-10 实测(发布管理减负 P0-3)

  • 界面:审核区块新增「全选可批准(N)」「批量批准选中(N)」「批量驳回选中(N)」;建批按钮改为「创建并锁定清单」。
  • 证据(建批即锁定):勾选 2 条已批准 → 填名 → 一次点击 → 批次 rb-95210ddcc14bbcb6afee323acd87b945 (验收-批量两项-20261010)出来就是 sealed(2 项、sealed_by=admin),没有单独的"锁定"这一步; 且复用同一份冻结版本 rcs-028ac704…(计划摘要未变 → 同版本复用)。
  • 判据修正:界面「可批准」原先把已驳回的也算进去(review_state !== 'approved'),已改为只含 pending + 门禁通过—— 已驳回的不会被"全选"顺手翻回批准。
  • 语义边界(集成测试固化):批量批准按人工身份写入,人工可推翻自己先前的驳回; system:auto 发起的批量批准对人工驳回过的那条必须跳过(Decided=0 / Skipped=1)。

C6 建批时冻结 + 自动批准 ★2026-10-10 实测(发布管理减负 P0-1 / P0-2)

  • 期望:不再有 生成新版本 / 确认冻结 两步;对内知识验收通过后由系统自动批准;建批即绑定一份已确认版本。
  • 证据(P0-2,界面 + 库):界面里给 ATOM-备忘录笔记.adoc 标「对内」→ 待入库 → 开始处理; 处理完成(completed / unpublished,验收 chunks_present=passed、knowledge_exists=passed、retrieval_hit=manual_required) → 审核记录 rv-934f2a64… / approved / reviewer_ref=system:auto / approved_for_release(未点任何人工批准)
  • 证据(P0-1,界面 + 库):勾选该条 → 填名 → 创建草稿批次(一次点击,无任何冻结步骤) → 批次 rb-a1c0012153e476f9a37e9c6d5f97cbe4(draft)自动绑定 rcs-028ac7045f9d2e533b28402b391accdf (confirmed、112 项、绑定时刻确认)→ 清单内含 ATOM-备忘录笔记.adoc / add / can_release=true
  • 对照:同一场景(批准晚于上一次冻结)在今天上午必然 422 release change set must be confirmed and cover every requested publication
  • 单文档交互次数:今天上午 ~9–11 次(含两次踩顺序陷阱)→ 现在 ~4–5 次(勾选、填名、建批、锁定、授权)

C5 测试库侧移除(解析监控 → 需下架 → 逐条下架)★2026-10-10 首次实测

  • 入口:解析监控 → 需下架 车道逐条列出「来源侧被判禁止导入、但知识仍存活在测试库里」的条目 (GET /api/v1/test-publications?removal=required,口径与车道计数同源)→ 每条 下架(从测试库移除) → 页内二次确认(取消在左、确认在右)→ 走既有 test.remove 工作流(删除测试库知识并读回校验)
  • 期望:条目置 disabling → 工作流完成后 disabled;车道计数递减;生产知识库不受影响
  • 证据(2026-10-10,两条都经界面走完):
    • 点击前车道 需下架(2):相机线束固定孔位尺寸图.pdf、DH-AG95夹爪适配-指令协议.md(与直接查库的候选一致)
    • 第 1 条(PDF):点下架 → 车道回到 1 → test_publications.retrieval_state=disabled(03:57:03Z); jobs test.remove / completed / attempt 2(第 1 次 test_remove_pending=远端仍在处理,重试通过); 工作流以 GetKnowledge 读回 404 判成功
    • 第 2 条(md):点下架 → 确认态出现但未执行(核库:仍 unpublished、移除任务数仍 1)→ 点确认 → disabling → disabled(04:08:55Z);job job-67e7e62c… completed(attempt 2)
    • 界面终态:需下架(0) + 空态文案;页面提示「已受理下架…」
  • ⚠️ 过程中踩到并修掉 Bug #7(二次确认按钮出现在原按钮位置,连点即误执行;已把「取消」放左) 与 Bug #8(jobs 部分唯一索引 + 空串 business_* → 第二次移除被静默吞掉、条目永久卡死;已改 NULL + 落行校验)

C3 查看结果 → 回滚

  • 入口:发布管理 → 查看结果 → 需要时 回滚 → 确认语句 rollback-production-release
  • 期望:回滚后目标库该知识停用(不删知识行),wiki 页保持
  • 证据:rx-f1d1d669… → rolled_back,authorized_by=admin,prior_execution_id=rx-dd47920a…;目标库该知识 enable_status=disabled、chunks 4/0 enabled、wiki 9 页完整
  • 证据(2026-10-10 界面实测):输入 rollback-production-release → 执行 rx-71d13453f5c684a7fa6f9c8337610d78 → rolled_back(1/1、failed 0、项级 rolled_back);页面 回滚 已回滚、每项结果 App账号注册.adoc → 已恢复上一版本 · 目标库 0b217e8a… · 已恢复; 独立核对目标库:knowledges 行仍在、parse_status=completed、enable_status=disabled ✓(首发布回滚=停用,不删行)
  • 证据(2026-10-10 第二遍,.md):回滚 + rollback-production-release → 执行 rolled_back(1/1、failed 0、项级 rolled_back); 独立核对目标库:行仍在(title DH-AG95夹爪适配-指令协议.md.md)、parse_status=completed、enable_status=disabled ✓
  • 证据(2026-10-10 第三遍,PDF):执行 rx-21be23f095075701aba8a62d58f3dd7b → rolled_back(1/1、failed 0); 独立核对目标库:相机线束固定孔位尺寸图.pdf.pdf 行仍在、parse_status=completed、enable_status=disabled ✓
  • 状态:✅ 2026-10-08 / 2026-10-10 实测(三份文档各一次发布+回滚,目标库均为"停用不删行")

C4 重排失败执行

  • 入口:发布管理 → 失败执行 → 重排 → 确认语句 retry-production-release
  • 期望:只重置失败项;verified / rolled_back 项保持完成;重排不改写 AuthorizedBy(历史不可篡改)
  • 证据:internal/httpapi/production_action_test.go 双通道用例
  • 状态:✅ 单测覆盖 + 2026-10-10 真实失败项实测(上面 C2 的失败执行即用 重排 + 固定语句 retry-production-release 重跑成功;同一条执行号 rx-63b81560… 由 failed 转为 completed, authorized_by=admin 未被改写)

D. 系统设置

D1 服务配置(测试 / 生产端点与 ID)

  • 入口:系统设置 → 服务配置(管理员)
  • 规则:只配四项(测试地址、生产地址、生产 ID、知识库 ID);保存时真探测,无效才拒;凭据只写不读
  • 状态:✅(见 P41/P42 与 ui_settings_* 测试)

D2 测试知识库绑定(对内 / 对外)

  • 入口:系统设置 → 测试知识库绑定 → 选库 → 保存绑定
  • 期望:保存立即生效并重排"还没上传"的待入库项;对内 ≠ 对外
  • 状态:✅ 本期实测

D3 生产知识库绑定(源库 → 目标库)

  • 入口:系统设置 → 生产知识库绑定 → 对内/对外各选一个生产库
  • 期望:发布写入被冻结到该项的 target_knowledge_base_id;未绑定时按同 ID 语义并在契约检查处失败关闭
  • 状态:✅(P8_PRODUCTION_KB_MAPPING_ACCEPTANCE.md;迁移 000044)

D4 存储与备份(整库逻辑备份的定时)★本期新增

  • 入口:系统设置 → 存储与备份(管理员)
  • 步骤:设置 定时时间 + 储存天数(+ 每月归档保留、启用开关)→ 保存并下发到主机;另可 重新下发当前设置 / 立即备份一次
  • 期望:保存即写库并下发到目标主机 cron;页面显示主机上实际生效的计划、cron 条目、最近一次备份与"已一致/待下发"
  • 证据(面板原文):目标主机 weknora-test · 已连接、主机 cron 服务 active、主机 cron 条目:30 03 * * * root /bin/bash /opt/weknora-test/logical-backup.sh >> …、主机上生效的计划 每天 03:30 · 保留 7 天 · 每月 3 份、与设置是否一致 已一致、最近一次备份 20261009T060736Z / 34 秒 · 742.7 MiB / 覆盖 8 库 / 失败 无
  • 实测改动往返:面板改 04:05 / 14 天 → 主机 /etc/cron.d/kb-logical-backup 变 05 04 * * *、参数文件 TIME=04:05 / KEEP_DAYS=14 ✓ → 再改回 03:30 / 7 天 → 主机 30 03 * * * ✓
  • 立即备份:按钮变"备份进行中…"→ 约 50 秒后自动刷出新结果(异步 202 + 在途单飞,重复点击不会跑两次)
  • 状态:✅ 2026-10-09 面板实测(写操作仅管理员:PUT/POST /api/v1/backup-schedule*)

D5 刷新策略

  • 入口:系统设置 → 刷新策略(文档工程师可改)
  • 期望:文档/资源刷新间隔、停滞判定、finalizing 时长;保存先验证再原子启用、旧版本留审计
  • 状态:✅(ui_settings 版本化激活测试)

D6 成员与角色

  • 入口:系统设置 → 成员与角色(管理员)
  • 期望:成员增删、角色(viewer/engineer/admin);管理员初始密码首登强改
  • 状态:✅(users 接口与角色网关)

E. 备份与恢复(运维流程)

E1 发布/导入前留档(PG 行 + ES 文档)

  • 入口:命令行 bash scripts/target_kb_snapshot.sh(默认云内执行)
  • 期望:$KB_BACKUP_ROOT/target-kb-snapshots/<UTC>-<kb8>/ 出现 8 张表 JSONL + es_documents.jsonl.gz + MANIFEST.json(sha256/每库 ES 计数) + export.sql + README.md
  • 证据:实测 2 分 52 秒(PG 8 表 44 秒;两库 ES 131,122 条 / 692 MB);PULL_ES=1 拉回本机 19 分 40 秒
  • 状态:✅ 2026-10-09 实测

E2 发布前硬门禁

  • 期望:写目标库之前必须存在覆盖该库、24h 内、PG 与 ES 两层齐备且哈希可校验的留档;否则 release_target_snapshot_missing(不可重试),目标库一行不碰
  • 证据:go run ./cmd/guardcheck backups/target-kb-snapshots <kb_id> → 两个绑定库 PASS、未绑定库 BLOCK;门禁单测 10 条
  • 状态:✅ 已上线(release-worker 已带此门禁)

E3 回填演练(证明"存了能回去")

  • 入口:python3 scripts/restore_drill.py --snapshot-dir <留档> --target-url <本地测试库>
  • 期望:行数与清单逐一致、抽样页面正文/标题逐字段相同、清理后残留 0
  • 证据:13,539 行全部回填成功;8 张表与清单逐一致、冲突 0、抽样页 388 字符一致、残留 0(实测值)
  • 状态:✅ 2026-10-09 实测

E4 整库逻辑备份(自管,落自己盘上)

  • 入口:主机 bash /opt/weknora-test/logical-backup.sh(手动)或按 D4 的定时
  • 期望:$KB_BACKUP_ROOT/rds-logical/<UTC>/<库>.dir(pg_dump -Fd -j4 -Z6)+ MANIFEST.json(含 failed_databases);按天轮转
  • 证据:33 秒 / 742.6 MiB / 214 文件 / 失败 0,覆盖 8 个库;RDS 管理库 rdsadmin 自动跳过;中断的运行改名 *.incomplete
  • 状态:✅ 2026-10-09 实测

E5 其余三层(现状)

  • ES 快照:仓库 kb_backup(宿主 $KB_BACKUP_ROOT/es-snapshots)+ SLM kb-hourly(3 天)/kb-daily(7 天);首个快照 36 秒 / 4.7 GB / 0 分片失败 ✅
  • OSS 版本控制:桶 dobot-weknora-test 已开启(覆盖/删除可按 VersionId 回滚)✅
  • RDS 托管备份:由控制在控制台自管(按你的决定:本期不做,不阻塞)⏳
  • 备份目录契约:scripts/backup_layout.sh 定义 /srv/kb-backups/{target-kb-snapshots,rds-logical,es-snapshots,logs},换主机(未来的生产云主机)跑同一份 ✅

F. 本期已知限制与边界(验收时不必当缺陷)

  1. ES 留档字节默认留目标主机(PULL_ES=0);异地副本不做(你的决定)。
  2. ES 快照仓与留档同机同盘:整机故障需靠 RDS 托管备份 + OSS 还原。
  3. wiki 通道的真实跨环境发布已跑通(2026-10-10,见 C2):发现并修复"写入保留外来源页、校验却要求集合相等"的口径冲突(eabe55b);降级保留(degraded=true)现在是发布常态路径。 3b. 指标条的健康探测有误报:解析监控 顶部把 Embedding 模型 / LLM 模型 / OSS / Redis-MQ / WeKnora API 显示为"阻断",但本期入库与发布全链路(上传/解析/索引/发布/回滚)都实际成功 → 探测口径待校正(不作阻塞项)。 3c. 系统设置 → 服务配置 的"测试端"探测报 404:test_weknora_base_url 为空时 api 用 TEST_WEKNORA_BASE_URL=http://app:8080 探测,而 app 在治理栈网络内不可解析 → 显示"可达·不可接单";worker 走的是可用路径(本清单 B1/C2 均成功),仅探测口径问题。 3h. 「禁止导入」未进入发布计划的期望策略 → 生产知识下不了架(已修):禁止导入写的是 source_items.policy_state='blocked',但"期望策略/desired_policy"的解析只看 source_item_scope_overrides / directory_scope_rules / source_items.visibility,两处都不看 policy_state: ① 发布计划(release_plan.go)把被禁止且生产侧已存在的条目算成 unchanged/present、can_release=false → 永远不给 delete; ② 建批的 delete 就绪判定(release_batches.go 的 releaseDeleteReadyCTEs)同样口径 → 即使计划给了 delete,创建封批仍被拒 422 (实测原文:"创建封批失败(HTTP 422)")。修复=两处都把 policy_state='blocked' 算进期望策略(同一口径,各一行); 新增集成测试用 policy_state(真实路由的写法)复现,并同时断言"计划出 delete"与"delete 批次可创建", 回退任一处修复该测试即 FAIL ✓。附带效果:从未导入却被标为禁止的 82 条"等待测试"噪音不再占发布计划(195 → 113 项)。 注意:现有 release_authorization 集成测试用 source_item_scope_overrides 造 blocked(计划能识别), 所以一直没暴露这个"测试与现实写法不一致"的偏差。 3g. deploy-api-web.sh 会静默关掉生产门禁(已修):脚本的 COMPOSE_FILES 默认只叠 compose.yaml, 重建 api 时若不叠 compose.release-gates.yaml,PRODUCTION_RELEASE_REQUESTS_ENABLED/PRODUCTION_BACKUP_RESTORE_VERIFIED 变 false,生产授权接口静默关闭,页面只在点「授权发布」时回一句 production release requests are disabled。 修复:脚本加前置保护 —— 正在运行的 api 已开闸而本次未叠门禁文件时直接拒绝 (REFUSED_WOULD_CLOSE_PRODUCTION_GATES,并打印保留门禁的正确命令;确要关闸显式 ALLOW_GATE_OFF=1), 结尾新增 === 7b 回显重建后的两道门禁状态。三种情形已按同一段判定逻辑验证 ✓ 3f. 集成测试曾改到线上设置(已修 + 已恢复数据):internal/httpapi/ui_settings_integration_test.go 只判断 TEST_DATABASE_URL 是否存在、没有 schema 隔离,而它就做「创建候选并激活」——直接跑在共享/线上库上时, 会把真实的 active 设置挤成 retired。2026-10-10 排查车道问题时踩到:线上 active 变成测试负载 (30/60/3600/14400 + https://weknora.prod.example:8443/weknora),真实的 uis-d40ea1fc… (15/30/1800/7200 + 生产端点 https://weknora-test.dobot.cn/)被 retired。 处理:先备份 backups/ui-settings/ui_setting_revisions.pre-restore.*.jsonl,再用单事务(先 retire 再 activate, 唯一约束 uq_ui_setting_active 只允许一条 active)恢复,两条 update 各影响 1 行,并复核线上值 ✓; 根因修复=给该用例加 per-test schema 隔离(照 internal/monitoring),复跑后线上设置不再被改动 ✓。 规矩:任何"创建并激活"的集成测试必须建独立 schema;只判 TEST_DATABASE_URL 存在就写共享库的用例一律视为危险。 3i. 测试库侧的移除仍无页面入口 已闭环(2026-10-10):解析监控 → 需下架 现在逐条列出条目, 每条可「下架(从测试库移除)」并二次确认(POST /api/v1/test-publications/{id}/removal,engineer 权限、 写路由需 Idempotency-Key、按条目幂等);实测两条走通(见 C5)。 3e. 发布/回滚结果页不自动刷新:Release Worker 完成后,发布管理 的执行行仍显示"待执行"(首次与第二遍都遇到), 需手动 刷新当前页面/reload 才看到 已完成/已回滚。后端状态是对的(DB 已是终态),属展示层轮询缺口。 3d. release_executions.snapshot_verification_id 为空:发布前快照门禁确实生效(缺少/过期会 release_target_snapshot_missing),但执行行没有记下"用的是哪份快照",追踪性待补。
  4. "重排"在页面上无真实失败项可点(单测覆盖);指标条"失败 1"是旧 artifact.fetch 死信遗留。
  5. 自管逻辑备份不能替代 PITR(RDS 控制台那条本期不做)。

验收建议顺序

  1. D4 存储与备份(本期新增,步骤最短、证据最全)→ 2. E1/E2/E3/E4(留档→门禁→回填→整库)→ 3. C2/C3(授权发布 → 回滚,需闸门开启)→ 4. B1/B2(入库与补救)→ 5. A1–A3、D1–D3、D5、D6(既有流程,按各自专题文档逐条核)。