Skip to content

Commit 7e93b5f

Browse files
committed
docs(openkal): record the layering correction that produced 0.2
Version 0.1 let the implementation provide the module a consumer imports, with the specification package providing openkal.decl.<name> for it to re-export. Review identified this as a contradiction of what a specification is for, and the identification was correct. The justification does not survive examination. That arrangement existed solely to make optional operations detectable through argument-dependent lookup, which requires an implementation's declarations to be visible to the consumer — and version 0.1 defined no optional operation. The mechanism served a requirement that did not exist, and it was paid for with a real cost: the name a consumer relies upon sat outside the specification's control. 0.2 restores the intended layering, verified: a backend that exports no module builds and runs, and a missing implementation is reported by the linker naming the undefined functions. This is the third occurrence of one shape in this round. The first was openkal.namespace, which merged two kinds of resource in order to unify naming; the second was extending cfg() with capability predicates, which carried an openarch conclusion into openkal. Each designed a mechanism for a requirement that did not yet exist. A requirement that arrives later can be met by deferring the decision and recording the constraints, which costs far less than implementing the wrong mechanism early.
1 parent fdd6862 commit 7e93b5f

2 files changed

Lines changed: 87 additions & 22 deletions

File tree

.agents/docs/2026-08-20-openkal-design.md

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# openkal 设计方案:通用内核 ABI 规范
22

3-
**状态**:**0.1 已实施并发布**。规范与声明模块在
3+
**状态**:**0.2 已实施并发布**(0.1 的分层已被 review 推翻,见 §21)。规范与接口模块在
44
[`mcpplibs/openkal`](https://github.com/mcpplibs/openkal),Linux 参考实现在
5-
[`mcpplibs/openkal-linux`](https://github.com/mcpplibs/openkal-linux),两者均为 `0.1.0`
5+
[`mcpplibs/openkal-linux`](https://github.com/mcpplibs/openkal-linux),两者均为 `0.2.0`
66
实施计划与结果见
77
[`2026-08-20-openkal-implementation-plan.md`](2026-08-20-openkal-implementation-plan.md)
88

@@ -1196,3 +1196,52 @@ extern "C" {
11961196
| ⚠️ 不做的事 | 它**不算**第三方后端,也不算第三个后端(linux/bare 是官方的两个) |
11971197
11981198
⇒ 判据仍然是**别人来不来**,而这份实现让「来」这件事从一个季度变成一个周末。
1199+
1200+
---
1201+
1202+
## 21. ⚠️ 0.1 的分层被推翻:实现不该拥有应用 import 的名字
1203+
1204+
0.1 让**实现**提供 `openkal.stream`,规范包提供 `openkal.decl.stream` 由实现再导出。
1205+
review 指出这与「openkal 是接口」矛盾 —— **应用 import 的那个名字落在了规范管不到的一方手里**。
1206+
1207+
### 21.1 我的理由为什么站不住
1208+
1209+
那套安排的**唯一**用途是让可选能力可经 ADL 探测,而 ADL 要求实现的声明对消费者可见。
1210+
⚠️ **而 0.1 一个可选能力都没有** —— `write_vectored` 是我为了演示机制放进去的。
1211+
1212+
⇒ **机制服务的是一个当时不存在的需求**,却付出了「实现拥有接口名」这个真实代价。
1213+
1214+
### 21.2 0.2 的分层(实测成立)
1215+
1216+
```
1217+
应用 ──import──► openkal ◄──import── 实现
1218+
│ │
1219+
└──────────── 链接 ─────────────────┘
1220+
```
1221+
1222+
- 规范包提供 `openkal.types` / `.abort` / `.stream` / `.memory`
1223+
- **实现零模块**,只贡献定义;它 import 与消费者**同一个**接口,因为它要定义自己声明的东西
1224+
- ⇒ **实现无法扩展接口** —— 这不是需要执行的规则,是这个安排的推论
1225+
1226+
ⓘ 实测:后端零模块的工程编译运行通过;缺实现时报
1227+
`undefined reference to kal_stream_write`(链接期,可读)。
1228+
1229+
### 21.3 代价与去向
1230+
1231+
| | 0.1 | 0.2 |
1232+
|---|---|---|
1233+
| 缺实现 | 编译期,点名模块 | **链接期**,点名函数 |
1234+
| 可选能力探测 | ADL,编译期 `if constexpr` | **无**(0.2 无可选能力) |
1235+
| 实现能否扩展接口 | 能加重载,靠 conformance 封 | **不能**,由安排本身排除 |
1236+
| 碎片化风险 | 有,需 surface diff 兜底 | **无** |
1237+
1238+
⇒ 可选能力的机制**推迟到真的出现时再定**,SPEC 6.3 记录了两个候选与各自的约束
1239+
(以及那三条实测:限定名 vs 非限定名、ADL 到不了未 import 的模块、点号延伸导致模块自环)。
1240+
1241+
### 21.4 元结论
1242+
1243+
这是本轮第三次同一形状:**我为一个尚不存在的需求设计了机制,并为它付出了真实代价。**
1244+
1245+
前两次是 `openkal.namespace`(为统一命名合并了两种资源)与 `cfg(mmu)`(把 openarch 的
1246+
结论搬进 openkal)。⇒ **判据应当是「今天有没有这个需求」,而不是「将来会不会有」** ——
1247+
将来的需求可以用「推迟决定并记录约束」来接,代价远低于提前实现一个错的机制。

.agents/docs/2026-08-20-openkal-implementation-plan.md

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ specification itself, which is maintained in the `mcpplibs/openkal` repository.
1010

1111
| Repository | Contents | Version |
1212
| --- | --- | --- |
13-
| `mcpplibs/openkal` | the specification, the modules that declare it, the surface checker, and a substitution example | 0.1.0 |
14-
| `mcpplibs/openkal-linux` | the reference implementation for Linux, its conformance suite, and an example | 0.1.0 |
15-
| `mcpplibs/mcpp-index` | descriptors for both packages | pull request 220 |
13+
| `mcpplibs/openkal` | the specification, the modules that declare it, the surface checker, and a substitution example | 0.2.0 |
14+
| `mcpplibs/openkal-linux` | the reference implementation for Linux, its conformance suite, and an example | 0.2.0 |
15+
| `mcpplibs/mcpp-index` | descriptors for both packages | pull requests 220 and 221 |
1616

1717
Both packages are mirrored to GitCode, and the mirrored archives were verified
1818
to be byte-identical to those served by GitHub.
@@ -51,16 +51,30 @@ the resolution is recorded.
5151

5252
### 3.1 Architecture
5353

54+
⚠️ **A correction applied after 0.1 was published.** Version 0.1 placed the
55+
module a consumer imports under the control of the implementation: the
56+
specification package provided `openkal.decl.<name>` and the implementation
57+
re-exported it as `openkal.<name>`. Review identified this as a contradiction of
58+
what a specification is for, and the identification was correct. The arrangement
59+
existed to support detection of optional operations through argument-dependent
60+
lookup, which requires an implementation's declarations to be visible to the
61+
consumer — and version 0.1 defined no optional operation, so the mechanism
62+
served nothing that existed.
63+
64+
Version 0.2 restores the intended layering: the specification package provides
65+
the interface, and an implementation contributes definitions and exports no
66+
module. The cost is that a missing implementation is reported by the linker
67+
rather than by the compiler, and the diagnostic names the undefined functions.
68+
5469
An interface is the unit of provision and of versioning. An implementation
5570
provides an interface in whole or not at all, and an interface that is not
5671
provided is absent as a module rather than present and refusing.
5772

5873
The consequence for the module layout is stated in clause 4 of the
59-
specification: the implementation owns the name a consumer imports, because
60-
argument-dependent lookup does not reach a module the translation unit has not
61-
imported. The alternative arrangement, in which the specification package owns
62-
that name, was rejected because it would have made optional capabilities
63-
undetectable.
74+
specification: the specification package owns every module, and an
75+
implementation exports none. An implementation therefore cannot extend the
76+
interface, and this is not a rule that must be enforced — it follows from the
77+
arrangement.
6478

6579
### 3.2 Stability
6680

@@ -76,22 +90,24 @@ reproduced by a test suite.
7690

7791
### 3.3 Simplicity
7892

79-
The capability mechanism uses argument-dependent lookup and a fallback overload.
80-
An earlier design used a record of capability flags together with a separate
81-
configuration file, and both were removed. A record can disagree with the code
82-
it describes; a declaration cannot. The removal also eliminated a second
83-
configuration format from the ecosystem, in which every other fact about a
84-
package resides in `mcpp.toml`.
93+
Version 0.2 defines no optional operation, and therefore no mechanism for
94+
expressing one. Two designs were built and removed before this was recognised: a
95+
record of capability flags with a separate configuration file, and a set of
96+
fallback overloads detected through argument-dependent lookup. Each solved a
97+
problem the specification did not yet have.
98+
99+
Clause 6.3 records the alternatives and the measurements that constrain them, so
100+
that the choice is informed when an optional operation is first defined.
85101

86102
### 3.4 User experience
87103

88-
A consumer that calls an operation the implementation does not provide is
89-
rejected during compilation, and the diagnostic carries the wording the
90-
specification supplies. This is the default behaviour and requires nothing of
91-
the consumer: no capability test, no configuration, and no annotation.
104+
A consumer imports the interface and names no implementation. Which
105+
implementation supplies the definitions is decided in the manifest, and changing
106+
it is a change to one line.
92107

93-
A consumer that wishes to adapt rather than fail uses the concept the interface
94-
provides, which evaluates to false when the operation is absent.
108+
A consumer that depends upon no implementation compiles and fails to link, with
109+
the undefined functions named. That is later than a compilation failure and is
110+
legible; clause 4.2 records it rather than concealing it.
95111

96112
### 3.5 Compatibility
97113

0 commit comments

Comments
 (0)