Files
curriculum-project-hub/spec/Spec/Courseware/Lesson.lean
T
sjfhsjfh 4a828e07b9 feat(spec): add lesson model contract (ADR-0005)
First product-core concept in the semantic master spec: a curriculum
engineering file is one lesson, modeled as an ordered sequence of typed
element instances over an open kind universe.

- docs/adr/0005: lesson model decision narrative + deferred OPEN items
- spec/Spec/Courseware/Lesson.lean: Primitives carrier (KindId/ElementData/
  TargetId/RenderRule kept abstract), Element, Lesson, RenderConfig, and the
  first checker seed (WarnsIgnored: used-kind-with-no-render-rule => warning)
- wire Spec.lean -> Spec.Courseware; drop placeholder Basic.lean

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 23:11:47 +08:00

116 lines
5.1 KiB
Lean4
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/-!
# Lesson —— 课程工程文件的母本模型(单节课)
本文件固定 ADR-0005 的骨架共识:一个**工程文件 = 一节课(lesson)**,
lesson 是**有类型 element 实例的有序序列**;element 实例 = kind 标签 + 符合该
kind schema 的数据;export target 是对 lesson 的投影;渲染由一份
**(kind × target) 配置矩阵**承载。
本轮**只承诺关系结构**。所有尚未决定的基元(schema 怎么表示、kind 怎么定义、
render rule 的载荷长什么样)都收口进 `Primitives`,以抽象类型留白(`OPEN`),
契约因此对未定之物**零承诺**,同时仍 type-check 通过。完整的"合法 lesson"判定
亦未定;本轮只落 checker 的第一颗种子(缺渲染 ⇒ warning)。
-/
namespace Spec.Courseware
/--
契约基元载体(`PINNED` 关系结构 / `OPEN` 基元留白,`ADR-0005`)。
把本轮**尚未决定**的基元收口到一处,使 lesson 模型在其上参数化。每个字段都是
一个 `OPEN` 点:契约只声明它**存在**、以及它在关系中扮演的角色,绝不假设其内部
表示。具体表示待后续各自的 ADR 落定后再 refine。
-/
structure Primitives where
/--
element 的**种类标识**(`PINNED` 开放宇宙 / `OPEN` 表示,`ADR-0005`)。
kind 构成一个**开放、可扩展的宇宙**(stdlib default + 远期第三方),因此**刻意
用一个抽象类型,而非封闭 `inductive`/枚举**——把"kind 集合不封闭"这条 PINNED
直接编码进类型。`KindId` 的具体表示是 `OPEN`。
-/
KindId : Type
/--
某 kind 的**合法数据**类型(`PINNED` 依赖关系 / `OPEN` schema 表示,`ADR-0005`)。
以 `KindId` 为索引的**依赖类型**,精确表达"element 数据必须**符合该 kind 的
schema**":不同 kind 的合法数据类型不同。kind 对框架的最小契约**就是**这份
schema(渲染不在其中)。schema 自身**怎么表示**是 `OPEN`,故此处只给出
`KindId → Type` 这一依赖,不触碰其内部。
-/
ElementData : KindId Type
/--
**导出目标标识**(`PINNED` 角色 / `OPEN` 表示,`ADR-0005`)。
一个 target(学生讲义 / 教师教案 / PPT / 第三方平台 archive / html 教具平台…)
是对同一 lesson 的一种投影。target 同样是开放集合;其具体表示 `OPEN`。
-/
TargetId : Type
/--
**渲染规则的载荷**(`OPEN` 表示,`ADR-0005`)。
"某 kind 在某 target 下如何呈现"的那条规则的**内容**。其表示完全 `OPEN`(typst
只是渲染后端之一,不是定义语言)——本轮只关心规则**在不在**(见 `RenderConfig`),
不关心它**是什么**。
-/
RenderRule : Type
variable (P : Primitives)
/--
**element 实例**:一个 kind 标签 + 符合该 kind schema 的数据(`PINNED`,`ADR-0005`)。
`data` 的类型由 `kind` 决定(`P.ElementData kind`),从类型上保证"数据符合该 kind
的 schema"——构造一个 `Element` 时不可能给出与其 kind 不匹配的数据。
注:ADR-0005 的 (a) 类信息(逐字稿、重点圈划等"语义性但 target-specific"的字段)
属于具体 kind 的 schema 范畴,故落在 `P.ElementData` 内,不在此通用结构上出现;
(b) 类信息(html 交互、build/npm)不进 element。
-/
structure Element where
/-- 该 element 的种类(`PINNED`,`ADR-0005`)。 -/
kind : P.KindId
/-- 符合 `kind` schema 的数据(`PINNED`,`ADR-0005`)。 -/
data : P.ElementData kind
/--
**lesson = element 实例的有序序列**(`PINNED`,`ADR-0005`)。
用 `List` 编码"有序":**次序是有意义的**。**时长不建模**——lesson 是内容编排
(讲义/PPT 式的内容流),不是时间轴。课程/单元不是 lesson,而是 lesson 的编排
(`OPEN`,见 ADR-0005 Deferred),不在本文件。
-/
abbrev Lesson := List (Element P)
/--
**渲染配置矩阵**:(kind × target) → 可选的渲染规则(`PINNED`,`ADR-0005`)。
这份矩阵**住在工程文件里**:框架给好 default,文件可逐项 override。`rule k t = none`
表示该 (kind, target) 对**尚无渲染规则**(此处只建模规则的**有无**,不建模其内容)。
-/
structure RenderConfig where
/-- (kind, target) ↦ 该对是否配了渲染规则及其载荷(`PINNED`,`ADR-0005`)。 -/
rule : P.KindId P.TargetId Option P.RenderRule
/--
kind `k` 在 target `t` 下**已配渲染规则**(`PINNED`,`ADR-0005`)。
即矩阵在该 (kind, target) 处非空。"覆盖"= 有规则可用;`covers` 为假即"缺渲染"。
-/
def RenderConfig.covers {P : Primitives} (c : RenderConfig P) (k : P.KindId) (t : P.TargetId) : Prop :=
(c.rule k t).isSome
/--
**checker 种子规则**:lesson 在 target `t` 下**存在被忽略的 element**(`PINNED`,`ADR-0005`)。
成立 ⟺ lesson 里**存在**某 element,其 kind 在 `t` 下没有渲染规则。
ADR-0005 PIN 住该情形的**严重级别 = warning,而非 error**:它**不阻断**导出,只提示
"这些 element 在该 target 下被忽略了"。这是 checker(产品里"站在 Lean 位置"的那个
东西)的第一颗诊断种子;完整的"合法 lesson"判定仍 `OPEN`。
-/
def WarnsIgnored (l : Lesson P) (c : RenderConfig P) (t : P.TargetId) : Prop :=
e l, ¬ c.covers e.kind t
end Spec.Courseware