/-! # Primitives —— Courseware 契约的留白基元 课程工程文件模型(ADR-0005)依赖一组基元:element kind 怎么标识、某 kind 的数据 schema 是什么、export target 怎么标识、渲染规则的载荷长什么样。 把它们收口成一个载体 `Primitives`,让 lesson 模型在其上参数化——契约因此能谈 element / lesson / 渲染**之间的关系**,而把每个基元的**内部表示**留给实现。注意: "某基元的语义已 PINNED"与"其表示进 Lean"是两回事——schema 的**形态**已由 ADR-0006 钉死(声明式 JSON Schema + 富内容叶子 = typst content),但那是 JSON / typst 的内部 结构、属实现细节,**不入 Lean**;Lean 只锚定"数据符合 kind schema"这条关系,故这些 基元在此仍以抽象类型承载。富内容这一新概念的 prose 母本见 `Courseware.RichContent`。 -/ namespace Spec.Courseware /-- Courseware 契约基元载体(关系 `PINNED`, ADR-0005;各基元表示留给实现, ADR-0006)。 -/ structure Primitives where /-- element kind 的标识(`PINNED` 开放宇宙, ADR-0005;表示 `OPEN`)。 **刻意用抽象类型而非 `inductive`**:ADR-0005 决定 kind 是开放可扩展宇宙(stdlib default + 第三方),封闭枚举会直接违背它。这与 `RunState` 的"未封闭"不同——此处 开放是**已决策的**。 -/ KindId : Type /-- 某 kind 的合法数据类型(`PINNED` 依赖关系, ADR-0005;schema 形态 `PINNED` ADR-0006, 表示仍抽象)。 以 kind 为索引:不同 kind 的合法数据类型不同。这正是 ADR-0005"kind 的最小契约 = 数据 schema"的载体——`ElementData k` 即"符合 `k` 之 schema 的数据"。 schema 的**形态**已由 ADR-0006 定:声明式 **JSON Schema**,字段类型 = 内置标量 + 一个 `content` 扩展类型;`content` 字段的值是一段 **typst 源**(canonical 存为带 虚拟路径的源,语义取其 module body)。但这些是 JSON / typst 的内部结构——**实现 细节,非分歧点**,不进 Lean。故 `ElementData` 在此**仍是抽象类型**:契约只承诺 "存在一个由 kind 决定的合法数据类型",不复刻 JSON Schema / typst Content 的形状。 -/ ElementData : KindId → Type /-- export target 的标识(`PINNED` 角色, ADR-0005;表示 `OPEN`)。一个 target 是对 lesson 的一种投影(讲义/教案/PPT/平台 archive…)。 -/ TargetId : Type /-- 渲染规则的载荷(表示 `OPEN`, ADR-0005)。 "某 kind 在某 target 下如何呈现"那条规则的**内容**。typst 是渲染后端之一(`PINNED`, ADR-0005)、不是定义语言;但渲染管线/规则载荷的具体表示仍 `OPEN`。契约只关心规则 **在不在**(见 `Render`),不碰它是什么。 -/ RenderRule : Type end Spec.Courseware