设计原则

SOW 用来约束所有权、派生状态、发布与证据的一组不变式。

SOW 首先不是一个“元数据生成器”,而是一个所有权与状态迁移系统,只是它的输出恰好是 APT 与 RPM 仓库。下面这些原则让整套系统保持可推理。

每个持久事实只有一个主人

每类持久事实都只有一个作用域与权威:

事实所有者
包体与软件包身份Repository
Desired 成员关系与 Built 状态Repository
Generation 与 ChangesetRepository
发布尝试与已应用 CheckpointRepository + target prefix
远端 inventory、grace 与删除证据Repository + target prefix

状态不会在 Repository 或发布前缀之间暗中共享。因此同一个包可以在不同 Repository 或 target 中各存一份;这是有意为之:本地去重不能制造分布式所有权。

正典数据与可重建投影

pool/ 中的包体是正典数据;协议索引、架构视图、报告与兼容导出都是投影。只有当 SOW 能证明输入集合完整且所有权明确时,投影才可以重建;它永远不会成为包体的第二个主人。

由此得到一条简单删除规则:投影只能由创建并记录它的操作移除;正典数据只有在所有 live 与 retained owner 的可达性都计算完成后才允许移除。

公共树是交付单元

Repository 根由 pool/ + dists/ 组成;整棵树才是静态托管、复制、鉴权与发布单元。 某个 RPM 架构 leaf 可以被客户端消费,但它不是一个独立拥有状态的 Repository。

sow.yml.sow/、锁、journal、凭据与恢复文件都是私有状态,绝不能随公共树对外服务。

包体做准备,指针做提交

发布顺序固定为:

包体 -> 不可变/校验和命名的元数据 -> 可变指针 -> 宽限期 -> 删除

包体与不可变元数据可以提前写入而暂不可见;repomd.xmlReleaseInRelease 这样的 协议指针才是提交边界。只有新指针已经持久化、旧读者与缓存窗口已经关闭后,旧对象才可删除。

恢复服从持久证据

提交意图之前,如果精确 reconcile 能证明公共指针从未改变,操作可以放弃;提交意图之后 只能前向恢复。SOW 不猜测一次半完成发布“应该已经成功”,而是比较 journal、manifest、 checkpoint、供应商身份与公共树。

证据互相矛盾时操作必须停止。一次可见的拒绝,比仓库历史悄悄分叉更安全。

兼容性是一张矩阵

协议合规、普通客户端、镜像工具、对象存储布局、代理规范化与文件系统语义是不同问题。 SOW 分别记录它们,并用与结论对应的真实客户端或供应商验证。

因此发布前的 C2 原型可以为了 reposync 采用硬链接视图;v0.2.0 则为了让远端每个包只存一份, 明确把默认 reposync 移到显式外部兼容导出中处理。

证据不会自动升级

规格不是实现,单测不是真实客户端结果,本地 Hugo 构建也不是公网发布。日期化结果始终绑定 当时的源码 revision、环境与版本;布局变化后,必须重跑对应门禁,不能把旧 PASS 直接继承。

非目标让模型保持诚实

v0.2.0 不承诺跨 Repository 去重、重叠多写者、bucket 全局协调、任意第三方镜像工具兼容, 也不在供应商缺少原子条件删除时承诺安全远端删除。这些排除项属于安全契约,不是“尚未补完”。

最后修改:2026-08-08: docs: align site with SOW v0.2.0 (9c98f8c)