全栈知识治理平台首次入库(P1-P10 + 发布链路模拟验证闭环): - 治理核心:源目录/变更集/人工审核/发布批次/发布执行(fail-closed 闸口) - 测试通道:WeKnora 测试 KB 上传/轮询/验收;poll 完成时写入 parser_generation/index_generation,修复 manual/seed 路径发布闸口永久拦截 - 发布适配器 v071(本地 source=测试角色 / 云端 target=生产角色 模拟验证通过): * ES 导出/导入 mapping 自适应(.keyword 子字段、可选 tenant_id) * OSS locator 支持嵌套 scheme;chunks.seq_id 由目标序列分配 * bundle 归档同身份重试原子替换;verifier 按 bundle chunk_type 计数 * 回滚 job payload 支持 prior_execution_id(strict 解码) - 三通道发布+回滚演练实测:publish 4/4 verified,检索命中, rollback 4/4 rolled_back,目标端 PG is_enabled=false、ES is_enabled=false - 前端 A·Data-console 风格全站统一,120 用例通过 - 门禁:go vet / go test 全包通过,gofmt 干净
6.7 KiB
P4.2 来源只读扫描与变化入账验收
范围
本切片将 PP 本地历史快照接入普通 Worker 的持久扫描链路:
来源管理手工触发
→ 原子创建 source_scan + source.scan.pp Job
→ 普通 Worker 只读 ledger/manifest/制品元数据
→ 与 613 条治理 Source Item 对账
→ 原子保存聚合、变化记录与连接健康
→ API/Web 展示扫描历史和变化结果
本切片不上传、移动、重命名或删除 PP 文件,不调用 PP 网络写接口,不删除治理对象或 WeKnora Knowledge。missing 只记录为 unavailable,54 个 over_limit 项继续阻断,生产 Release Worker 保持关闭。
安全配置与装配
PP_SCAN_ENABLED默认false;仅显式启用且 allowed root、ledger、manifest 三个路径完整时注册source.scan.ppHandler。- allowed root 必须是绝对规范路径;ledger 和 manifest 必须位于其内。
- Scanner 拒绝越界、软链接、非普通文件、缺少
local_path、打开期间文件身份变化和超过字节上限的元数据文件。 over_limit记录允许其根目录内的制品不存在,因为它从未被下载;若制品存在仍必须通过普通文件和非软链接校验。- Compose 只给普通 Worker 挂载 PP 数据目录,挂载模式为只读;API、Web、Release Worker均没有该挂载。
- 实际容器探针:ledger/manifest 可读,向 manifest 追加写入被只读文件系统拒绝。
- 本地测试 WeKnora Key 仍只挂载给普通 Worker;本切片不调用 WeKnora 上传或删除 API。
Migration 5
Migration 5 新增:
source_scans
source_scan_changes
idx_source_scans_one_active
idx_source_scans_connection_created
idx_source_scan_changes_pending
关键约束:
- 每个来源连接最多一个
pending/runningScan; - Scan 状态限定为
pending/running/completed/failed; - Change 类型限定为
added/updated/unavailable; snapshot_sha256只在内部审计,不进入 API;- down migration 先删除依赖的 changes 表,再删除 scans 表。
一次性 PostgreSQL 执行了:
up to 5 → down to 4 → up to 5
result: passed
持久库迁移前逻辑备份:
tmp/backups/governance-pre-migration5-20260906T011223+0800.dump
bytes=242987
mode=0600
pg_restore catalog=valid
持久库应用后:
schema_migrations=1,2,3,4,5
source_items=618
content_objects=535
source_item_versions=618
test_publications=535
旧业务数据计数未因 Migration 5 改变。
API
POST /api/v1/source-connections/{id}/scan
GET /api/v1/source-connections/{id}/scans?limit=20&offset=0
GET /api/v1/source-connections/{id}/scans/{scan_id}
触发语义:
- 成功返回
202; - 同一连接已有活动扫描返回
409; - 不存在或不可扫描的连接返回
404; - 创建 Scan 和 Job 在同一条 PostgreSQL data-modifying CTE 中完成。
安全只读投影允许:
Scan: id/state/聚合计数/固定错误码/创建完成时间
Change: id/source_item_id(可空)/change_type/observed_size_bytes(可空)/created_at
明确省略:
snapshot_sha256
stable_external_id
previous_revision
observed_revision
allowed root、ledger/manifest/local_path
来源 URL、正文、base_url、secret_ref、Token/API Key/Cookie
跨连接读取 Scan 详情返回 404。前端再次使用 allowlist 重建对象,不透传服务端额外字段。
真实 PP 只读扫描
真实输入只读基线:
ledger events=1093
manifest/source items=613
existing artifacts=559
historical over_limit without downloaded artifact=54
第一次真实扫描按安全规则失败:
state=failed
error_code=pp_snapshot_invalid
根因是初版 Scanner 把 54 个历史 over_limit 项未下载的制品也要求为已存在普通文件。修复后规则为:over_limit 缺失制品是合法阻断状态,但路径仍必须声明且位于 allowed root 内;其他状态缺失仍 fail-closed。
修复后的多次真实扫描均一致:
observed=613
unchanged=559
added=0
updated=0
over_limit=54
missing=0
planned_uploads=0
changes=0
PP ledger、manifest 和来源制品未被修改;没有生成上传计划。
调度、并发与恢复
单活动扫描
Worker 停止时连续触发同一连接:
first=202
second=409
active_scans=1
恢复 Worker 后唯一活动扫描完成。
pending 恢复
Worker stopped
→ POST creates pending Scan/Job
→ Worker started
→ Scan completed, Job completed
过期 running 租约重领
真实持久队列构造“旧 Worker 已领取但完成前崩溃”的状态:
before: scan=running job=running attempt=1 lease_expired=true
restart persistent Worker
after: scan=completed job=completed attempt=2
result: observed=613 unchanged=559 over_limit=54
Handler 对 running Scan 可恢复;若 Scan 已经 completed 而 Job 尚未完成,则重领为幂等 no-op,不重复扫描或写 Change。
最终状态:
source_scans=6
completed=5
failed_fixed_code=1
active_scans=0
active_source_scan_jobs=0
Web 与 Firefox
“来源管理”保留左侧导航和顶部标签,并新增:
- 来源连接的最近扫描聚合;
- “立即扫描”按钮及活动态禁用;
- 最近 20 条扫描历史;
- 单次扫描变化记录安全详情;
- 零变化时明确显示“本次扫描未发现新增、更新或不可用变化”。
Firefox headless 1440×1200 验收:
history_rows=6
completed=5
failed=1
detail_selection=passed
empty_changes_message=visible
safe_projection_leaks=0
root_overflow=false
table_overflow=false
截图:tmp/P42_SOURCE_SCAN_HISTORY_FIREFOX.png
视觉复核确认:导航、顶部标签、5 条完成/1 条失败、观察数、上限阻断和变化面板均清晰;无横向裁切、遮挡或乱码。
自动化验证
PP配置安全默认和路径校验:passed
Worker Handler装配测试:passed
File Scanner边界与over_limit回归:passed
Store扫描触发/完成/失败/恢复/安全读取:passed
HTTP触发/历史/详情/404/409安全投影:passed
前端allowlist解析、请求和页面渲染:passed
一次性PostgreSQL真实扫描与变化读取:passed
Migration 5 up/down/up:passed
Web tests:31/31 passed
TypeScript typecheck:passed
Vite production build:passed
Compose config:passed
Compose全镜像build:passed
Go全仓库test:passed
Go vet:passed
Go cmd build:passed
gofmt差异:0
Firefox真实页面:passed
运行边界
- 本地治理平台与本地测试 WeKnora 范围内完成;
- PP 来源只读,未调用来源写/删 API;
- 未删除 PostgreSQL 或 WeKnora 卷;
- 未连接或修改生产环境;
RELEASE_WORKER_ENABLED=false;- 54 个
over_limit项继续阻断; - 未 commit、未 push。