# 用户操作流程与验收清单 按流程逐项验收用。每条给出:**入口 → 用户步骤 → 期望 → 验收证据 → 状态**。 状态标记:**✅ 已实测**(附证据)|**⏳ 待验收**|**⚠️ 已知限制**。 深度细节见各自专题文档(`P3…`/`P5…`/`P6…`/`P8…`/`PRODUCTION_BACKUP_RECOVERY.md`/`WIKI_CO_TRANSPORT…`)。 统一入口:**http://127.0.0.1:8088/**(测试栈;右侧面板可直接操作)。角色:管理员 / 文档工程师 / 只读。 --- ## A. 内容进入 ### A1 取件(钉钉知识库 / PP 清单) - 入口:**来源与目录 → 内容来源**(勾选条目;钉钉**扫码会话失效时先刷新会话**——注意:走 dws 取件后钉钉这条不再依赖浏览器会话) - 步骤:选来源 → 展开目录 → 勾选 → 取件(后台入队) - 期望:解析监控出现"处理中";失败进**死信**,可重排 - 证据:`解析监控 → 死信` 队列;重排轮次键 `artifact-fetch::r` - 状态:✅(死信重排修复 `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/-/` 出现 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 ` → 两个绑定库 `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//<库>.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**(既有流程,按各自专题文档逐条核)。