会拒绝的一致性

有一项合理的检验,可以把本体与带标签的示意图区分开:这件制品能禁止任何东西吗?没有公理、没有校验、没有架构意图的本体,不过是画成图的词汇表。它只能描述,不能拒绝。如果 Vercy 只是这样,那批评就击中了。

所以本页直接回答这项检验:能,并给出一个你可以亲自运行的演示。Vercy 的一致性是被强制执行的,不是装饰。确实有一个程序会读取模型、判定它无效,并以非零码退出,同时指名它违反了哪条规则。下面写明是哪个程序、哪条规则,以及一次完整推演的拒绝。

一致性是关卡,不是描述

发布在 github.com/ver-cy/registry 的活动登记册,是 ELMM 配置的运行实例。它的写入路径是 pull request,而这条路径上的准入关卡是持续集成:ci/run.py 按顺序运行七项 fail-closed 检查,任何一项变红都会阻断合并。fail-closed 意味着默认是拒绝:模型只有通过每一项检查才被接纳,而不是无人反对便予接纳。

这七项检查,每一项在第一次违规时便以非零码退出并指名规范规则:

这不是关于行为的承诺,这就是行为本身。关卡就是代码,而代码是公开的。

图完整性检查,逐条来看

check_graph 是大多数结构性拒绝发生的地方。它强制六条规则,每条都有自己的诊断信息与自己的 ELMM 标识:

  1. 每个主身份只对应一个节点(ELMM-I17):没有两条记录共用同一个登记册 id。
  2. 引用完整性(ELMM-I19):边的每一端,fromto,都是已注册记录的 id。
  3. 导出覆盖(ELMM-I11、ELMM-I19):边所指名的每个类型,都出现在目标记录的 exports 中。该图仅凭记录即可静态计算,因此指向目标未导出类型的引用,会在解析之前就被拒绝。
  4. 声明最小值一致(ELMM-I11):当某条记录的 requires 指名了与某条边相同的目标与类型时,该边的 declared_min 等于那个 min_version。在最小版本选择下,边是解析器唯一的输入,因此记录与边必须一致。
  5. 内核隔离(ELMM-I18):没有任何 composes 边离开内核节点。内核不编排任何东西:它对模型的了解来自登记册,绝不来自出边。
  6. 无环性(ELMM-I16):composes 子图是有向无环图。

其中任何一条都足以让一棵绿色的树变红。

一次完整推演的拒绝

拿规则 3,导出覆盖,故意把它弄坏。

假设唯一受治理的版图 vercy.plmm 声明它引用了一个 budget-line 类型,该类型归组织单元模型 vercy.oumm 所有。某位作者在 mmdg/edges.json 中添加:

{
  "from": "vercy.plmm",
  "to": "vercy.oumm",
  "edge_type": "references",
  "declared_min": "0.1.0",
  "compositional_role": "R4",
  "kinds": ["budget-line"]
}

这条边在模式上是有效的:必填字段一应俱全,因此 check_schema 放行。两端都已注册,因此引用完整性通过。它读起来像一条形式完全正确的陈述。但 vercy.oumm 并不导出 budget-line。它的导出是 org-unitreporting-lineestablished-positionunit-mandatebudget-line 不在其中。这条边主张了一项依赖,而目标从未发布过那份含义。

check_graph 拒绝。导出覆盖子检查遍历每条边,取目标的 exports 集合,并在第一个不在其中的指名类型上失败。它会输出目标的完整导出集合并排序,好让阅读者清楚看到什么被发布了、什么没有。该次运行以非零码退出,并给出这段诊断:

check_graph: FAIL
edge #N (vercy.plmm -references-> vercy.oumm) references kind 'budget-line'
which is not in vercy.oumm exports ['established-position', 'org-unit',
'reporting-line', 'unit-mandate'] (ELMM-I11, export coverage)

合并被阻断。不是标记,不是警告,而是阻断:ci/run.py 返回非零退出码,pull request 无法落地。要修复,作者要么让 vercy.oumm 真的导出 budget-line,那是一项真实的变更,须由所有者模型发布并为之负责;要么不再声称一项从未被授予的引用。登记册不会让一个模型依赖另一个模型未曾导出的含义。这就是这件制品所禁止的事。

同样形态的拒绝遍布整个关卡。循环组合,即版图组合了某个模型而该模型又传递地组合回它,会被无环性拒绝(ELMM-I16)。指向不存在模型的边,会被引用完整性拒绝(ELMM-I19)。requires 中与其边的 declared_min 不一致的最小值,会被声明最小值一致规则拒绝(ELMM-I11),否则解析器将选中一个记录从未据以校验过的版本。会迫使内核被编辑的注册,会被零改动保证拒绝(ELMM-I7)。带日期的文件名,会被命名约定拒绝(S1)。两次运行结果不一致的解析器,会被确定性规则拒绝(ELMM-I23)。每一种情况里,规则就是理由,标识写在消息中,退出码非零。

重点正是这种规律:每一次拒绝都指名一条规范规则,模型可以被指向它、与它争辩、依据它修正。没有沉默的拒绝,也没有凭喜好的拒绝。破坏规则、拿到退出码、读那个标识。

登记册之外:模型校验器

登记册把关的是模型之间的关系。单个模型自带各自的关卡。集体元模型发布并版本化于 github.com/ver-cy/collective-meta-model,随附一个节点校验器,运行 V0 到 V2 的关卡外加 CMM 专有检查,并对损坏的实例予以拒绝:没有集体的成员、没有授予者的权限授予、无法闭合问责回路的授权。该模型的注册记录把 V2 校验记为自己的一致性级别,而这条记录的价值取决于背后那个校验器的价值。正是校验器让这个级别成为一项主张,而不是一件装饰。

诚实的边界

有三件事必须说清楚。

第一,今天这是模型实例层面的一致性。这些检查拒绝无效的登记册记录、无效的边、非确定的解析,以及对照各自校验器无效的模型实例。它们在写入路径的 CI 中运行,任何人也都可以在本地运行:从 github.com/ver-cy/registry 克隆登记册,运行 python ci/run.py,破坏一条规则,看着它失败。这是真实的,也足以回答那项检验。

第二,托管式的公开校验服务,即一个页面或端点,让外来者丢进任意模型、不必克隆任何东西就能拿到裁定,目前还不存在。这是一个公开挂账的待办项。在它上线之前,拒绝是可复现的,但对陌生人来说还不是一键可得。

第三,为复现这次拒绝而克隆登记册的读者会发现,有一条参考实现记录仍带着中立化处理之前的商业关联标识。它对上文的演示没有影响,因为演示只使用中立的 vercy.* 标识,但把那条记录及其边中立化,是登记册上一项已挂账的待办。

这些说明没有一条会改变对那项检验的回答。在一件制品能够拒绝之前,它只是词汇表。这就是拒绝:一项被指名的检查、一个非零退出、一条规范规则,以及一次没有发生的合并。你可以在登记册的 CI 与模型校验器中亲自运行。两者都是公开的。