Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .spec/decisions/0006-capability-keys-have-no-raw-constructor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# 0006 · Capability key 只由生成注册表投影,不保留裸构造器

- 日期:2026-08-29
- 状态:生效

## 背景

D-015 裁决(架构源 ADR-040 §7.1 / ADR-048)把 capability **键空间**定为
`ids/index.json` 独有,架构生成器是唯一发射方,下游只消费投影;裁决前作为
「唯一正确模型」的仓内私有键值表就此变成违规。

本仓 `CapabilityKey` 当时是 `from_local_index(u32)` 公共构造器 + 本地序号,
正是被取代的那种模型:任何调用方都能凭空造一个注册表里没有的键,而且不会有
任何东西报错。

## 决策

`CapabilityKey` **不保留任何裸构造器**——crate 内外都没有。唯一构造路径是
`from_registered(ArchitectureCapabilityKey)`(及按 id 查表的
`from_registry_id`),而 `ArchitectureCapabilityKey` 只在
`cargo xtask gen-contracts` 生成的表里存在。

`gen-contracts` 同时做三方交叉核对:`ids/index.json` 的 `Capability` 命名空间、
镜像 C Header 的 `LUMIO_CAPABILITY_<SCREAMING>` 常量、以及
`LUMIO_CAPABILITY_COUNT` 必须逐值一致,任一不一致直接生成失败。
`LUMIO_CAPABILITY_BITS` 显式排除在键空间外:D-015 只裁了键,掩码还是计数、
以及任何 bit 位指派仍未冻结,`registry::capability_bits()` 保持为空。

## 后果

- 仓内无法表达一个上游没注册的 capability;真有需要必须先在架构源注册,接受
这条往返延迟。
- 测试只能用已发布的键(如 `Native` / `HybridCLR`),不能再用任意序号构造
用例,可读性反而变好,但重命名上游 id 会同时改到测试。
- 镜像半新半旧(改了 Header 忘了重跑生成器,或反之)在生成期即失败,不会拖到
运行期才发现。
1 change: 1 addition & 0 deletions .spec/decisions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@
| [0003](0003-ffi-buffer-classes-and-leases.md) | FFI Buffer 三分类 + 异步租约,按 provenance 定释放方 | 生效 |
| [0004](0004-job-state-machine-and-clock-port.md) | Job 状态机 CAS 线性化 + 私有单调时钟 port | 生效 |
| [0005](0005-codec-diagnostics-pending-and-dual-status.md) | codec/diagnostics 维持 pending,模块引入双状态标注 | 生效 |
| [0006](0006-capability-keys-have-no-raw-constructor.md) | Capability key 只由生成注册表投影,不保留裸构造器 | 生效 |
2 changes: 1 addition & 1 deletion .spec/knowledge/standards/repository-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ metadata:
- 逐 Entity/逐 Voxel/逐包 FFI 必须先证明批处理不足;不得把跨边界调用开销扩散到上层。
- 结果必须可诊断、可取消、可重复;不能以“调用方自行保证”替代契约,panic 必须在 ABI 边界转换为稳定错误。
- ABI/Capability/Error Schema、ID 与 Fixture 只在架构源维护;本仓消费已发布 Baseline,不复制生成器或第二套 Schema。
- Root ABI 消费机制(ADR-040 §7):上游发布物字节级镜像在 [`docs/architecture/abi/`](../../../docs/architecture/abi/README.md)(钉 revision + `.baseline.sha256` 钉 Hash),Rust 侧数值经 `cargo xtask gen-contracts` 从镜像生成,测试与镜像互证;ErrorCode 数值权威只有 `ids/index.json`Capability bitD-015)与非 `linux-x86_64-glibc` 布局(D-016)保持不绑定。
- Root ABI 消费机制(ADR-040 §7):上游发布物字节级镜像在 [`docs/architecture/abi/`](../../../docs/architecture/abi/README.md)(钉 revision + `.baseline.sha256` 钉 Hash),Rust 侧数值经 `cargo xtask gen-contracts` 从镜像生成,测试与镜像互证;ErrorCode 与 Capability **键**的数值权威只有 `ids/index.json`(Capability 键空间由 D-015 裁决,ADR-040 §7.1,仓内私有键值表即违规),Capability **bit**(掩码/计数与 bit 位,D-015 未裁)与非 `linux-x86_64-glibc` 布局(D-016)保持不绑定。
- 性能改动记录吞吐、p95/p99、分配、峰值内存、硬件/构建配置和结果确定性范围。
61 changes: 52 additions & 9 deletions crates/lumio-contract-types/src/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
//! four indices, never the Rust/C# generated packages.
//!
//! Still deliberately unbound (treated as absent, not inferred):
//! - `capability_bits` semantics and any bit position (D-015 pending);
//! - `capability_bits` semantics and any bit position (D-015 adjudicated the
//! capability *key space* only, ADR-040 §7.1; mask-vs-count stays open);
//! - any layout profile other than `linux-x86_64-glibc` (D-016 pending);
//! - an `OperationId` namespace (does not exist; identity is the published
//! (`apiTable[].name`, `slots[].slotIndex`) pair).
Expand All @@ -26,13 +27,13 @@ pub(crate) const GENERATED_CONTRACT_REVISION: &str = ARCHITECTURE_BASELINE_ID;

const ROOT_ABI_BUNDLE_ID: &str = "root-abi-v1";
const ROOT_ABI_BUNDLE_DIGEST: &str =
"03ca75361fed3ca95f8efd55af2e311ea8300b2635b590ae6d46394d58bc6a39";
"02dce705a9a6fe7a437ed2e4137b03de7341ed614f30f10b614659c5226184a7";
const ROOT_ABI_HEADER_DIGEST: &str =
"040451bbde5a4dec3726be5f5a7be4bb934c3f68a1ca87f9c55559cae738efc7";
"fa2aaca231b80ac5118dc13d3f4a4a50a1c16c844f09f8e2ac8d723af11c4352";
const ROOT_ABI_COMPILER_NAME: &str = "lumio-abi-compiler";
const ROOT_ABI_COMPILER_VERSION: &str = "1.0.0";
const ROOT_ABI_COMPILER_DIGEST: &str =
"217437fd4755e1a339e2029838cc4a2d2fb305fa05520c8cfd10ea98cc2ff290";
"0aaf61d65153aadc4ddda1b36fa1b7bfb38373d52e8ba3299457cefe16864bff";
const ROOT_ABI_INPUT_HASH: &str =
"696a58d0525b897b549dd1e432166ae1020835902a5984221a8e60d5d8285bb3";
const ROOT_ABI_LAYOUT_PROFILE_ID: &str = "linux-x86_64-glibc";
Expand Down Expand Up @@ -121,11 +122,53 @@ pub struct ArchitectureOperationId {
_private: (),
}

/// Capability-bit newtype. Uninhabited until D-015 lands: V1 freezes neither
/// mask-vs-count semantics nor any bit position, and the ID Registry
/// `Capability` numerics are CoreEngine package-capability enumeration
/// ordinals, not bit positions — deriving a key from either is forbidden
/// (ADR-040 §7).
/// One registered `Capability` value. D-015 (ADR-040 §7.1) adjudicated the
/// key space: `ids/index.json` is the sole authority and the architecture
/// generator its sole emitter, so instances exist only in the generated
/// registry tables and no caller can mint an unregistered key.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ArchitectureCapabilityKey {
id: &'static str,
numeric: u32,
status: &'static str,
}

impl ArchitectureCapabilityKey {
/// Only the generated registry tables construct instances.
pub(crate) const fn new(id: &'static str, numeric: u32, status: &'static str) -> Self {
Self {
id,
numeric,
status,
}
}

/// Registered id string, e.g. `"VoxelSpatial"`.
pub const fn id(self) -> &'static str {
self.id
}

/// Registered numeric: a 1-based enumeration ordinal, **not** a bit
/// position (D-015 froze the key space only).
pub const fn numeric(self) -> u32 {
self.numeric
}

/// Published lifecycle status, `"Active"` or `"Reserved"`.
pub const fn status(self) -> &'static str {
self.status
}

/// Whether the published status is `Active`.
pub fn is_active(self) -> bool {
self.status == "Active"
}
}

/// Capability-bit newtype. Uninhabited: D-015 adjudicated the key space only
/// (see `ArchitectureCapabilityKey`). V1 still freezes neither mask-vs-count
/// semantics for `capability_bits` nor any bit position, so reading a key is
/// allowed and deriving a bit is not (ADR-040 §7 / §7.1).
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct CapabilityBits {
_private: (),
Expand Down
15 changes: 14 additions & 1 deletion crates/lumio-contract-types/src/generated_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! (upstream revision in that directory's README). Regenerate with
//! `cargo xtask gen-contracts` after a mirror update; commit together.

use crate::generated::ArchitectureErrorCode;
use crate::generated::{ArchitectureCapabilityKey, ArchitectureErrorCode};
use crate::layout::{AbiStructGolden, AbiTypeGolden};

pub(crate) const ABI_POINTER_BYTES: u32 = 8;
Expand Down Expand Up @@ -105,3 +105,16 @@ pub(crate) const ERROR_CODES: &[ArchitectureErrorCode] = &[
ArchitectureErrorCode::new("PanicBoundary", 1052),
ArchitectureErrorCode::new("InternalInvariant", 1053),
];

#[rustfmt::skip]
pub(crate) const CAPABILITY_KEYS: &[ArchitectureCapabilityKey] = &[
ArchitectureCapabilityKey::new("Native", 1, "Active"),
ArchitectureCapabilityKey::new("HybridCLR", 2, "Reserved"),
ArchitectureCapabilityKey::new("ReferenceVoxel", 3, "Active"),
ArchitectureCapabilityKey::new("VoxelSnapshot", 4, "Active"),
ArchitectureCapabilityKey::new("VoxelStreaming", 5, "Active"),
ArchitectureCapabilityKey::new("VoxelSpatial", 6, "Active"),
ArchitectureCapabilityKey::new("VoxelMeshCollision", 7, "Active"),
ArchitectureCapabilityKey::new("VoxelAllResident", 8, "Active"),
ArchitectureCapabilityKey::new("VoxelVolatileChunks", 9, "Active"),
];
17 changes: 10 additions & 7 deletions crates/lumio-contract-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
//! 架构源已发布 baseline id `LGE-V1.4-2026-08-27` 与 ADR-040 Root ABI bundle;
//! 本仓登记为该 bundle 的 consumer,按 ADR-040 §7 直接绑定其 C Header 与四个
//! 索引(字节级镜像见 `docs/architecture/abi/`),**不**消费 Rust/C# 生成包。
//! ErrorCode 数值权威只有 `ids/index.json`(含 ADR-046 kernel band);
//! Capability bit 语义(D-015)、非 `linux-x86_64-glibc` 布局档(D-016)与
//! OperationId(不存在,B-ABI-004 不适用)保持不绑定,一律不得手写。
//! ErrorCode 与 Capability **键**的数值权威只有 `ids/index.json`(分别含
//! ADR-046 kernel band 与 D-015 裁决的键空间,ADR-040 §7.1);
//! Capability bit 语义(D-015 只裁键空间,掩码/计数与 bit 位仍未冻结)、
//! 非 `linux-x86_64-glibc` 布局档(D-016)与 OperationId(不存在,
//! B-ABI-004 不适用)保持不绑定,一律不得手写。

#![forbid(unsafe_code)]

Expand All @@ -19,8 +21,9 @@ pub mod layout;
pub mod registry;

pub use generated::{
AbiVersion, ArchitectureErrorCode, ArchitectureOperationId, CapabilityBits, ContractMismatch,
LumioBuffer, LumioCoreConfigV1, LumioHandle, LumioStatus, RootAbiBinding, StructSize,
abi_version, architecture_baseline_id, root_abi_binding, verify_generated_contract_revision,
verify_generated_contract_revision_against, verify_root_abi_bundle_digest_against,
AbiVersion, ArchitectureCapabilityKey, ArchitectureErrorCode, ArchitectureOperationId,
CapabilityBits, ContractMismatch, LumioBuffer, LumioCoreConfigV1, LumioHandle, LumioStatus,
RootAbiBinding, StructSize, abi_version, architecture_baseline_id, root_abi_binding,
verify_generated_contract_revision, verify_generated_contract_revision_against,
verify_root_abi_bundle_digest_against,
};
37 changes: 27 additions & 10 deletions crates/lumio-contract-types/src/registry.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
//! Read-only registry queries over the published ID Registry.
//!
//! `ids/index.json` is the sole numeric authority (ADR-040 §7); the table
//! consumed here is generated from its byte-pinned mirror by
//! `ids/index.json` is the sole numeric authority (ADR-040 §7); the tables
//! consumed here are generated from its byte-pinned mirror by
//! `cargo xtask gen-contracts`, so no numeric in this crate is hand-written.
//! Only the `ErrorCode` namespace is bound:
//! The `ErrorCode` and `Capability` namespaces are bound:
//!
//! - `Capability` numerics are CoreEngine package-capability enumeration
//! ordinals, not bit positions; deriving any kernel capability key from
//! them is forbidden until D-015 lands, so they stay unbound here.
//! - `Capability` keys are bound since D-015 (ADR-040 §7.1): the registry is
//! the key-space authority and the architecture generator its sole
//! emitter, so a repository-private key table is a violation. The numerics
//! are 1-based enumeration ordinals, **not** bit positions — reading a key
//! is allowed, deriving a bit is not, and `capability_bits` stays unbound.
//! - No `OperationId` namespace exists or is reserved (B-ABI-004 adjudicated
//! not applicable): the public identity of a callable operation is the
//! published (`apiTable[].name`, `slots[].slotIndex`) pair.
//! - `MessageType` / `FaultClass` are GameRuntime-owned and outside this
//! repository's consumption surface.

use crate::generated::{ArchitectureErrorCode, ArchitectureOperationId, CapabilityBits};
use crate::generated_data::ERROR_CODES;
use crate::generated::{
ArchitectureCapabilityKey, ArchitectureErrorCode, ArchitectureOperationId, CapabilityBits,
};
use crate::generated_data::{CAPABILITY_KEYS, ERROR_CODES};

/// Architecture error codes from the generated registry, in published order
/// (includes the ADR-046 kernel status band).
Expand All @@ -35,8 +39,21 @@ pub fn operation_ids() -> impl Iterator<Item = ArchitectureOperationId> {
core::iter::empty()
}

/// Capability bit entries. Empty until D-015 freezes the `capability_bits`
/// semantics and bit assignment (see module docs).
/// Registered capability keys from the generated registry, in published
/// order (ADR-040 §7.1); includes `Reserved` values, whose status callers
/// must honour rather than infer.
pub fn capability_keys() -> impl Iterator<Item = ArchitectureCapabilityKey> {
CAPABILITY_KEYS.iter().copied()
}

/// Look up one registered capability key by its published id string.
pub fn capability_key(id: &str) -> Option<ArchitectureCapabilityKey> {
CAPABILITY_KEYS.iter().copied().find(|key| key.id() == id)
}

/// Capability bit entries. Still empty after D-015: the adjudication froze
/// the key space only, leaving `capability_bits` mask-vs-count semantics and
/// every bit position unbound (see module docs).
pub fn capability_bits() -> impl Iterator<Item = CapabilityBits> {
core::iter::empty()
}
Loading
Loading