# 知识治理平台领域对象契约 ## 目的 本文件是知识治理平台当前实现的唯一领域对象映射。它将产品术语映射到现有Go模型、PostgreSQL表和外部边界;后续模块不得另造一套身份、时间或状态语义。 本文件不把“代码存在”“容器健康”写成已完成能力。尚未存在的对象或关联明确列在 [Current implementation gaps](#current-implementation-gaps)。 ## 统一主链 ```text SourceConnection → SourceItem → SourceItemVersion → ContentObject SourceConnection → SourceCatalogSnapshot → SourceCatalogNode SourceCatalogChangeSet → SourceCatalogChangeItem SourceItemVersion → TestPublication → AcceptanceResult ReleaseBatch → ReleaseExecution → ReleaseItemExecution ``` 完整目标主链将在后续工作包补齐为: ```text SourceConnection → SourceItem / SourceItemVersion / ContentObject → SourceCatalogSnapshot → SourceCatalogChangeSet → ProcessingExecution / Job → TestPublication / AcceptanceResult → ReleaseChangeSet → ProductionSnapshot → ReleaseBatch / ReleaseExecution ``` ## 全局身份与时间规则 | 规则 | 契约 | | --- | --- | | ID | 所有主对象使用服务端生成的不透明ID;页面和Job Payload只能传递ID,不能传递受保护来源URL、Token或Cookie。 | | 时间 | `created_at`表示治理对象创建;`updated_at`表示本对象最后治理状态变化;来源侧时间必须单独使用`updated_at_source`或`source_updated_at`。 | | 内容Hash | 只有`content_objects.sha256`是文件内容SHA-256。`canonical_uri_hash`、`source_locator_hash`和`snapshot_sha256`绝不能作为内容Hash展示或用于内容去重。 | | 删除 | 来源不可用不等于来源删除。只有权威来源删除事件或经人工确认的删除ChangeSet才可进入目标移除流程。 | | 目标写入 | 普通治理层不直接写WeKnora数据库;测试写入通过官方API/受控适配器;生产发布由独立Release Worker和门禁处理。 | ## Source **产品含义:**一个独立配置、独立健康状态、独立刷新策略的资料来源。 **当前实现:** | 项目 | 当前映射 | | --- | --- | | Go模型 | `governance.SourceConnection` | | 数据表 | `source_connections` | | 主键 | `source_connections.id` | | 类型 | `source_type`:当前为`pp`、`alist`、`dingtalk`;`manual`是Go层支持的扩展类型。 | | 时间 | `created_at`、`updated_at` | | 可变配置 | `enabled`、`schedule`、`last_health` | | 敏感字段 | `base_url`、`secret_ref`;不得返回前端或日志。 | **不可变/关系约束:** ```text 一个SourceConnection可以有多个SourceItem。 一个SourceConnection可以有多个SourceCatalogSnapshot。 SourceItem、Snapshot、ChangeSet不得跨SourceConnection关联。 ``` ## SourceFile **产品含义:**来源中的一份可治理资料,以及该资料的每个来源版本和内容对象。 SourceFile不是一个单表对象,当前由三个对象组成: | 逻辑层 | Go模型 | 数据表 | 稳定身份 | | --- | --- | --- | --- | | 文件身份 | `governance.SourceItem` | `source_items` | `(source_connection_id, stable_external_id)` | | 文件版本 | `governance.SourceItemVersion` | `source_item_versions` | `(source_item_id, revision)` | | 内容对象 | `governance.ContentObject` | `content_objects` | 全局`sha256` | **关键字段:** ```text SourceItem: - source_connection_id - stable_external_id - canonical_uri_hash - normalized_path - revision - updated_at_source - source_change SourceItemVersion: - source_item_id - revision - content_object_id(内容尚未下载或验证前允许为空) - content_state - source_updated_at ContentObject: - sha256 - size_bytes - mime - validated_format - local_spool_ref - content_state ``` **关系规则:** ```text 路径变化不必然产生新SourceItem。 内容Hash变化产生新的SourceItemVersion或新的内容关联。 同一内容Hash可以被多个SourceItemVersion复用,但每个版本仍保留自己的来源身份和来源时间。 TestPublication必须引用一个SourceItemVersion,不能直接引用路径或ContentObject。 ``` ## Snapshot **产品含义:**一次来源目录扫描获得的不可变目录事实;它记录当时看到的文件/目录,而不是文件正文副本。 **当前实现:** | 项目 | 当前映射 | | --- | --- | | 快照表 | `source_catalog_snapshots` | | 节点表 | `source_catalog_nodes` | | 当前指针 | `source_catalog_heads` | | 快照主键 | `source_catalog_snapshots.id` | | 来源约束 | `(source_connection_id, snapshot_sha256)`唯一 | | 时间 | `observed_at`、`created_at`;Head使用`activated_at` | | 目录节点 | `node_type=folder` | | 文件节点 | `node_type=file` | **Snapshot规则:** ```text SourceConnection → SourceCatalogSnapshot → SourceCatalogNode Head只指向一个当前快照;切换Head不得修改旧快照。 节点的source_locator_hash是来源定位摘要,不是内容Hash。 节点可选关联source_item_id;未关联节点仍可作为目录事实展示。 ``` **操作记录补充:**`source_scans`和`source_scan_changes`是旧扫描运行记录,不能替代当前不可变目录快照。 ## ChangeSet **产品含义:**两个不可变来源Snapshot之间的可审查差异计划。 **当前实现:** | 项目 | 当前映射 | | --- | --- | | 主表 | `source_catalog_change_sets` | | 条目表 | `source_catalog_change_items` | | Go投影 | `governance.CatalogChangeSet`、`CatalogChangeItem` | | 主键 | `source_catalog_change_sets.id` | | 快照关系 | `from_snapshot_id`、`to_snapshot_id` | | 动作 | `add`、`update`、`move_scope`、`delete`、`no_change` | | 时间 | `created_at`、`confirmed_at`、`applied_at` | | 当前状态字段 | `planned`、`confirmed`、`applied`、`cancelled` | **ChangeSet规则:** ```text 一个ChangeSet只能属于一个SourceConnection。 from_snapshot_id和to_snapshot_id必须属于同一SourceConnection。 to_snapshot_id当前唯一,因此一份激活快照最多对应一份目录ChangeSet。 ChangeItem当前使用(change_set_id, source_locator_hash)唯一。 ChangeSet只表达比较结果;它本身不等于已经上传、解析或删除。 ``` **后续目标:**S8/P3/P5会补充SourceItemVersion、内容Hash、旧新范围、确认执行和Job关联。 ## Job **产品含义:**可租约、可重试、可恢复的异步工作项;Job只保存安全业务ID和进度,不保存敏感连接数据。 **当前实现:** | 项目 | 当前映射 | | --- | --- | | Go模型 | `jobs.Job`(`internal/jobs`) | | 数据表 | `jobs` | | 主键 | `jobs.id` | | 幂等 | `idempotency_key`唯一 | | 调度 | `priority`、`available_at` | | 租约 | `lease_owner`、`lease_until` | | 尝试 | `attempt`、`max_attempts` | | 时间 | `created_at`、`updated_at`、`started_at`、`completed_at` | **当前Job类型示例:** ```text source.catalog.refresh test.upload test.poll test.validate test.remove release.publish release.rollback ``` **Job规则:** ```text Job完成只表示本地处理器完成;对于WeKnora解析,必须继续由test.poll确认远端终态。 Job payload不允许保存Token、Cookie、签名URL、原始文件内容或生产凭据。 ReleaseExecution通过job_id外键绑定发布Job。 ``` ## Release **产品含义:**把经过测试验收的知识,以可追溯、可验证、可回滚方式发布到生产。 当前Release由批次、执行和执行项组成: | 逻辑层 | Go模型 | 数据表 | 主键 | | --- | --- | --- | --- | | 发布批次 | `governance.ReleaseBatch` | `release_batches` | `id` | | 批次条目 | `ReleaseBatchItemView`投影 | `release_batch_items` | `(release_batch_id, publication_id)` | | 发布执行 | `governance.ReleaseExecution` | `release_executions` | `id` | | 执行条目 | `ReleaseItemExecutionView`投影 | `release_item_executions` | `id` | **Release关系:** ```text ReleaseBatch → ReleaseExecution → ReleaseItemExecution ReleaseExecution → Job(通过job_id) ReleaseItemExecution → TestPublication(通过publication_id) Rollback ReleaseExecution → prior publish ReleaseExecution ``` **Release规则:** ```text ReleaseBatch sealed后内容不可变。 一个批次最多有一个publish execution。 一个publish execution最多有一个rollback execution。 ReleaseItemExecution记录生产前后revision和安全错误码。 只有独立生产门禁、备份恢复验证和Release Worker均满足时才可以请求生产执行。 ``` ## TestPublication和验收(辅助对象) TestPublication不是原始SourceFile,也不是生产Release;它表示某个SourceItemVersion在指定测试知识库中的处理实例。 ```text SourceItemVersion → TestPublication → AcceptanceResult ``` | 项目 | 当前映射 | | --- | --- | | 测试发布 | `governance.Publication` / API投影`TestPublication` / 表`test_publications` | | 验收结果 | `governance.AcceptanceResult` / 表`acceptance_results` | | 身份 | `(source_item_version_id, test_kb_id)`唯一 | | 处理维度 | ingest_state、processing_state、retrieval_state | | 版本维度 | parser_generation、index_generation | ## 当前实现缺口 ### Current implementation gaps 以下缺口是已知事实,不能在任何页面、报告或测试中标为“已完成”: 1. `SourceCatalogNode`尚不能为所有来源绑定`SourceItem`和`SourceItemVersion`。 2. 目录Snapshot节点尚未持久保存文件内容SHA-256;`snapshot_sha256`不是文件内容Hash。 3. `SourceCatalogChangeItem`尚未保存旧新内容Hash、旧新范围和SourceItemVersion关联。 4. ChangeSet确认尚未直接创建Processing Job;当前Processing Confirmation仅绑定TestPublication。 5. Job尚未通过统一外键绑定ChangeSet、文件版本、目标和动作;该工作属于P3/P5。 6. `ReleaseChangeSet`和`ProductionSnapshot`尚未实现;当前Release Plan是实时计算,不能冒充不可变发布ChangeSet。 7. 当前ReleaseExecution没有来源Snapshot、来源ChangeSet、发布ChangeSet和生产Snapshot的完整外键链;该工作属于R1、R2、R6。 8. Source配置、SourceFile身份和内容Hash的运行时证据仍需后续S2、P1、P2补齐。 ## C1验收边界 C1只完成以下内容: ```text 冻结当前对象术语和真实映射 明确身份、时间和关系规则 标出当前不存在的对象和关联 用测试防止领域契约文档丢失关键对象 ``` C1不完成: ```text 统一状态机(C2) OpenAPI(C3) 写操作幂等(C4) 内容Hash闭环(P1) ChangeSet执行(P3) 发布ChangeSet或Production Snapshot(R1/R2) ```