# P2.2 PP历史账本离线接管验收 ## 范围 本阶段将旧PP同步器的append-only本地账本接入治理数据库: - 不请求PP、AList、钉钉或WeKnora网络API; - 不修改旧同步项目及其账本、manifest或下载文件; - 不下载、上传、移动或删除源文件; - 不启用新PP定时Connector; - 不将离线历史记录宣称为当前WeKnora已验证状态。 ## 折叠与恢复契约 1. 按旧脚本的`source_url_hash`稳定键折叠; 2. 只接受`completed`和带Knowledge ID的`content_alias`终态; 3. 单次账本只能包含一个KB; 4. 所有终态字段必须完整,source locator和SHA必须是64位小写十六进制; 5. 任何仅有中间状态、没有终态的source key都会阻断整批导入; 6. 缺SHA终态只能从匹配locator hash的manifest恢复; 7. 恢复前验证账本大小、manifest大小、实际文件大小和实际SHA; 8. 超过旧连接器50MiB单文件限制时阻断; 9. alias必须指向同一快照内的completed canonical,并且SHA、Knowledge ID一致; 10. 输出只含聚合数字,不输出KB ID、source ID、Knowledge ID、URL或本地文件路径。 ## 预期真实账本口径 ```text events=1093 source_items=559 completed=530 content_aliases=29 recovered_sha256=75 verified_bytes=871524116 content_objects=530 versions=559 publications=530 ``` `recovered_sha256=75`必须建立在75个文件逐字节重算全部匹配的前提上。 ## 数据库映射 ```text 1 source_connection enabled=false,secret_ref/base_url为空 559 source_items external + pp_public_tag + unchanged 530 content_objects verified,完整SHA唯一,不保存local_spool_ref 559 versions raw摘要只保留legacy_status、source_ids和alias canonical hash 530 publications uploaded + completed + unpublished ``` 29个alias只建立自己的SourceItem和Version,共享canonical ContentObject,不重复创建TestPublication。 ## 幂等和事务 - 整批导入在单个PostgreSQL事务内完成; - 只允许`INSERT ... ON CONFLICT DO NOTHING`; - 禁止UPDATE和DELETE; - 第一次真实导入期望新增`1/559/530/559/530`; - 第二次相同导入期望新增`0/0/0/0/0`; - 任一步失败必须回滚整批。 ## 自动验证 在项目根目录运行: ```bash /home/zheromiku/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.0.linux-amd64/bin/go test ./... -count=1 /home/zheromiku/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.0.linux-amd64/bin/go vet ./... /home/zheromiku/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.0.linux-amd64/bin/go build ./cmd/... ``` 真实数据库集成测试必须在唯一临时数据库中设置`TEST_DATABASE_URL`后运行;普通无环境测试显示SKIP不等于集成测试通过。 ## 本地库读回验收 必须验证: ```text schema_migrations=1,2 jobs=2 source_connections=1 source_items=559 content_objects=530 source_item_versions=559 test_publications=530 content_alias versions=29 unique publication knowledge IDs=530 forbidden raw fields=0 ``` 530条历史Publication的`retrieval_state`必须是`unpublished`。只有未来通过目标WeKnora只读API确认对象存在、状态和检索行为后,才能改为`enabled`。 ## 备份与运行边界 - 导入前创建PostgreSQL原生自定义格式备份; - 备份必须非空、权限600并通过`pg_restore --list`; - 导入只使用短期容器,不重启Compose五个长期服务; - 不执行`docker compose down -v`、卷删除、commit、push或外部部署。