diff --git a/spec/Spec/Courseware.lean b/spec/Spec/Courseware.lean index 684b20f..c8c4201 100644 --- a/spec/Spec/Courseware.lean +++ b/spec/Spec/Courseware.lean @@ -16,6 +16,6 @@ import Spec.Courseware.Open - **`Check`** —— checker 语义:`Severity` + 6 类诊断 + **合法 lesson = 无 error 级 诊断**(模型外设施诊断以抽象谓词 + `Oracle` 表示);检查管线的 5 阶段、序、compile 门控。 -- **`Open`** —— 留白骨架(核心关系 OPEN,已 surface 不臆造):题库 `QuestionBank`、 +- **`Open`** —— OPEN 骨架(核心关系 OPEN,已 surface):题库 `QuestionBank`、 课程编排 `Course`。 -/ diff --git a/spec/Spec/Courseware/Check/Diagnostic.lean b/spec/Spec/Courseware/Check/Diagnostic.lean index 60885cd..de68494 100644 --- a/spec/Spec/Courseware/Check/Diagnostic.lean +++ b/spec/Spec/Courseware/Check/Diagnostic.lean @@ -6,7 +6,7 @@ import Spec.Courseware.Export.Render 产品里"站在 Lean 位置"的 rule-based checker,语义在此沉淀(ADR-0010,经 ADR-0012 修订)。它对 lesson 提诊断,每条有**分类**(`DiagKind`)与**严重级别**(`Severity`)。 -本模块:钉级别类型(二分);钉 7 类诊断各自的含义与级别,并把"**合法 lesson = 无 +定级别类型(二分);定 7 类诊断各自的含义与级别,并把"**合法 lesson = 无 error 级诊断**"建成判定(ADR-0005 deferred 的"完整合法判定"的回填);对**模型外设施** 型诊断(typst 编过否、数据合 schema 否)用**抽象谓词 + `Oracle` 实现边界**表示——契约 说"存在这条诊断、什么意思、什么级别",真值由实现提供,不在 Lean 内计算(不内嵌 typst @@ -50,7 +50,7 @@ inductive DiagKind where /-- 每类诊断的**严重级别**(`PINNED`, ADR-0010)。六类 `error`(阻断);**唯 `renderIgnored` 为 `warning`**——ADR-0005 种子规则"缺渲染 ⇒ warning,不阻断导出"。 -钉成全函数使"哪类阻断"成为可引用、可对齐的事实(实现侧 `DiagCode` 级别据此对齐)。 -/ +全函数使"哪类阻断"成为可引用、可对齐的事实(实现侧 `DiagCode` 级别据此对齐)。 -/ def DiagKind.severity : DiagKind → Severity | .partPathMissing => .error | .unknownKind => .error diff --git a/spec/Spec/Courseware/Check/Pipeline.lean b/spec/Spec/Courseware/Check/Pipeline.lean index fa9beaf..2a27b5c 100644 --- a/spec/Spec/Courseware/Check/Pipeline.lean +++ b/spec/Spec/Courseware/Check/Pipeline.lean @@ -5,8 +5,8 @@ import Spec.Courseware.Check.Diagnostic checker 的 `check` 按**固定顺序**跑五个阶段,逐阶段收集诊断;`compile` 阶段有**门控**。 顺序与门控是契约——它决定用户看到哪些诊断(藏在缺文件背后的语法错,在文件补齐前不 -显示,这是有意的)。每阶段的**算法**不进 Lean(宪法第 5 条深度上限):只钉**阶段、序、 -门控**。阶段对应上游模块:`load`←`cph-model`;`structural`←part 路径/未知 kind; +显示,这是有意的)。每阶段的**算法**不进 Lean(深度上限:只定**阶段、序、 +门控**)。阶段对应上游模块:`load`←`cph-model`;`structural`←part 路径/未知 kind; `schema`←`cph-schema`;`compile`←`cph-typst`(模型外设施);`coverage`←`renderIgnored`。 -/ diff --git a/spec/Spec/Courseware/Export/Artifact.lean b/spec/Spec/Courseware/Export/Artifact.lean index 4e202a6..c30c931 100644 --- a/spec/Spec/Courseware/Export/Artifact.lean +++ b/spec/Spec/Courseware/Export/Artifact.lean @@ -2,7 +2,7 @@ # Artifact —— export target 的产物(ADR-0009 / 0011) ADR-0009:一个 export target 是**一次 build**,产出一个**有类型的产物**。ADR-0011 -钉死:产物是**带字段的 ADT**——"产物到底指什么"(单文件落在哪 / 一棵树产出哪些文件) +固定:产物是**带字段的 ADT**——"产物到底指什么"(单文件落在哪 / 一棵树产出哪些文件) 是不好猜的领域语义,必须写进字段 + doc,而非抹成两个空构造子。路径/glob 用 `String` 承载并由 doc 赋义(它们就是文本),不复刻文件系统类型。 -/ diff --git a/spec/Spec/Courseware/Export/Render.lean b/spec/Spec/Courseware/Export/Render.lean index ed71d5c..576dbe0 100644 --- a/spec/Spec/Courseware/Export/Render.lean +++ b/spec/Spec/Courseware/Export/Render.lean @@ -4,7 +4,7 @@ import Spec.Courseware.Export.Artifact /-! # Render —— export target = artifact + 有序 typed steps(ADR-0009 / 0011) -ADR-0009:export target 是一次 build,产出一个有类型的 `Artifact`。ADR-0011 钉死 build +ADR-0009:export target 是一次 build,产出一个有类型的 `Artifact`。ADR-0011 固定 build 的**形状**:一个 target 是 `artifact` + 一串**有序 typed step**。 - `typstCompile template` —— 把**模板文件**(如 `exports/student.typ`)编译成产物。它是 @@ -20,7 +20,7 @@ ADR-0009:export target 是一次 build,产出一个有类型的 `Artifact`。ADR **shell step 的执行语义(ADR-0013)。** `shell` 不再只是占位:它**会被执行**,语义是把 `run` 交给平台 shell、以**工程根为工作目录**运行,产物由被调外部工具自己写出(框架不装配 -内容)。三条边界是真分歧点,故钉契约: +内容)。三条边界是真分歧点,故定为契约: 1. **opt-in by construction** —— 任意命令执行只在用户**显式** build 一个 shell target 时发生, 绝不在 `check` 里跑。`check` 只校验结构(lesson 是否合法),不执行外部工具、不验其产物。 2. **失败归属** —— shell step 退出非零是一次 **build-过程失败**,不是 lesson 的合法性缺陷; @@ -46,7 +46,7 @@ namespace Spec.Courseware variable (P : Primitives) /-- 一个 build **step**(`PINNED` typed, ADR-0011;可扩展)。MVP 仅一个 `typstCompile`; -`steps` 是 list 因为 FileTree / 第三方 build 会需多步。刻意不把模板内部、shell 命令的 +`steps` 是 list 因为 FileTree / 第三方 build 会需多步。不把模板内部、shell 命令的 解析结构写进来(实现细节, ADR-0011 OPEN)。 -/ inductive Step where /-- 编译模板文件 `template`(相对工程根)成产物;框架注入 manifest。typed 的理由: diff --git a/spec/Spec/Courseware/Model.lean b/spec/Spec/Courseware/Model.lean index 31df449..c1be6be 100644 --- a/spec/Spec/Courseware/Model.lean +++ b/spec/Spec/Courseware/Model.lean @@ -7,7 +7,7 @@ import Spec.Courseware.Model.Info /-! # Courseware.Model —— 工程文件的内容模型 -留白基元(`Primitives`)、富内容锚点(`RichContent`)、原子单位(`Element`)、单节课 +基元(`Primitives`)、富内容锚点(`RichContent`)、原子单位(`Element`)、单节课 (`Lesson`)、课时元信息(`Info`:canonical author 为列表 vs `RawInfo` 撰写态)。 决策出处 ADR-0005 / 0006 / 0008。 -/ diff --git a/spec/Spec/Courseware/Model/Element.lean b/spec/Spec/Courseware/Model/Element.lean index 19bc8ac..92bf401 100644 --- a/spec/Spec/Courseware/Model/Element.lean +++ b/spec/Spec/Courseware/Model/Element.lean @@ -3,7 +3,7 @@ import Spec.Courseware.Model.Primitives /-! # Element —— 课程内容的原子单位 -ADR-0005:element 实例 = 一个 kind 标签 + 符合该 kind schema 的数据。本模块把它编码成 +ADR-0005:element 实例 = 一个 kind 标签 + 符合该 kind schema 的数据。把它编码成 依赖结构,使"数据必须匹配其 kind"成为类型层面的事实而非运行时校验。 -/ diff --git a/spec/Spec/Courseware/Model/Info.lean b/spec/Spec/Courseware/Model/Info.lean index c900220..850f1b4 100644 --- a/spec/Spec/Courseware/Model/Info.lean +++ b/spec/Spec/Courseware/Model/Info.lean @@ -5,7 +5,7 @@ 基数**是一个真分歧点:一节课可由多人(教研组)署名,故 canonical 模型里 author 是一个 **有序列表**,不是单值或可选单值。 -另有一条值得钉的模式:on-disk 的**撰写态**(用户实际填写的形态)是**语法糖**——单作者可写 +另一条模式:on-disk 的**撰写态**(用户实际填写的形态)是**语法糖**——单作者可写 `author = "…"`,多作者写 `author = ["…", "…"]`——但这个"字符串或数组"的二态**只活在加载 边界**:`RawInfo` 经归一化折叠成 canonical `Info`,其后不再出现。canonical 接收端始终是 `List String`,raw 形式不泄漏进模型其余部分。这正是 `Info`(canonical)与 `RawInfo` @@ -24,7 +24,7 @@ inductive RawAuthor where | many (names : List String) /-- raw 作者归一化为**有序作者列表**(`PINNED`, ADR-0008)。单作者 ⇒ 单元素列表;数组 -⇒ 原样。这条钉死"canonical 接收端始终是 `List String`"。 -/ +⇒ 原样。canonical 接收端始终是 `List String`。 -/ def RawAuthor.normalize : RawAuthor → List String | .one n => [n] | .many ns => ns diff --git a/spec/Spec/Courseware/Model/Primitives.lean b/spec/Spec/Courseware/Model/Primitives.lean index 656ae8b..eff743d 100644 --- a/spec/Spec/Courseware/Model/Primitives.lean +++ b/spec/Spec/Courseware/Model/Primitives.lean @@ -1,26 +1,26 @@ /-! -# Primitives —— Courseware 契约的留白基元 +# Primitives —— Courseware 契约的基元 课程工程文件模型(ADR-0005)依赖一组基元:element kind 怎么标识、某 kind 的数据 schema 是什么、export target 怎么标识。收口成载体 `Primitives`,让模型在其上参数化 ——契约谈得了 element / lesson / 渲染**之间的关系**,而把每个基元的**内部表示**留给 -实现。注意:某基元语义已 PINNED(如 schema 形态由 ADR-0006 钉死)与其表示进 Lean +实现。注意:某基元语义已 PINNED(如 schema 形态由 ADR-0006 固定)与其表示进 Lean 是两回事——JSON Schema / typst 的内部结构属实现细节,不入 Lean,故基元在此仍以抽象 -类型承载。富内容的 prose 母本见 `Courseware.RichContent`。 +类型承载。富内容的母本见 `Courseware.RichContent`。 -/ namespace Spec.Courseware /-- Courseware 契约基元载体(关系 `PINNED`, ADR-0005;各基元表示留给实现, ADR-0006)。 -/ structure Primitives where - /-- element kind 标识(`PINNED` **开放宇宙**, ADR-0005;表示 `OPEN`)。刻意用抽象 + /-- element kind 标识(`PINNED` **开放宇宙**, ADR-0005;表示 `OPEN`)。用抽象 类型而非 `inductive`:ADR-0005 决定 kind 是开放可扩展宇宙(stdlib + 第三方), 封闭枚举会违背它——此处开放是**已决策的**(区别于 `RunState` 的"尚未封闭")。 -/ KindId : Type /-- 某 kind 的合法数据类型(`PINNED` 依赖关系, ADR-0005;schema 形态 `PINNED` ADR-0006,表示仍抽象)。以 kind 为索引:`ElementData k` 即"符合 `k` schema 的 - 数据"。schema 形态(声明式 JSON Schema + `content` 叶子 = typst 源)是 ADR-0006 - 钉死的,但属 JSON/typst 内部结构、实现细节,不进 Lean;契约只锚定"数据符合 + 数据"。schema 形态(声明式 JSON Schema + `content` 叶子 = typst 源)由 ADR-0006 + 固定,但属 JSON/typst 内部结构、实现细节,不进 Lean;契约只锚定"数据符合 kind schema"这条关系,故此处仍是抽象类型。 -/ ElementData : KindId → Type /-- export target 标识(`PINNED` 角色, ADR-0005;表示 `OPEN`)。一个 target 是一次 diff --git a/spec/Spec/Courseware/Model/RichContent.lean b/spec/Spec/Courseware/Model/RichContent.lean index d1a8899..2c9b3ba 100644 --- a/spec/Spec/Courseware/Model/RichContent.lean +++ b/spec/Spec/Courseware/Model/RichContent.lean @@ -1,5 +1,5 @@ /-! -# RichContent —— 富内容(ADR-0006 的 prose 母本) +# RichContent —— 富内容(ADR-0006 的母本) ADR-0006:element schema 的"叶子"可以是 `content` 类型,其值是一段**源文本**, 按其 **format** 决定语义(ADR-0015)。两种 format: @@ -13,7 +13,7 @@ ADR-0006:element schema 的"叶子"可以是 `content` 类型,其值是一段** 的一等文件,坐落在一个**虚拟路径**上;相对 import 限本工程路径结构内 + `@package`(不跨工程)。markdown format 的富内容不参与 typst 求值,但同样由一个虚拟路径定位(供 markdown 装配 step 按序读取,见 `Export/Render`)。 -本模块只立 prose 锚点 + 最小抽象签名:typst 的 `Content`/`Module` 内部结构、JSON Schema 形状、format 的 +只立锚点 + 最小抽象签名:typst 的 `Content`/`Module` 内部结构、JSON Schema 形状、format 的 具体判别属实现细节,不进 Lean,只承诺"富内容由一个虚拟路径定位"+"叶子带 format"这两条关系。 -/ @@ -33,8 +33,8 @@ inductive ContentFormat where | markdown /-- 对一段富内容的**引用**:它坐落在某个虚拟路径上(`PINNED` 关系, ADR-0006),并带一个 -**format**(`PINNED`, ADR-0015)。刻意**不**建模源文本、不建模求值出的 `Content`(那是实现侧的事); -只钉"富内容经由一个 `VPath` 定位 + 带 format",作为 `Primitives.ElementData` 里 `content` 叶子的语义锚点。 -/ +**format**(`PINNED`, ADR-0015)。不建模源文本、不建模求值出的 `Content`(那是实现侧的事); +"富内容经由一个 `VPath` 定位 + 带 format",作为 `Primitives.ElementData` 里 `content` 叶子的语义锚点。 -/ structure RichContentRef where /-- 该富内容所在的虚拟路径(ADR-0006;落盘后为真实相对路径, ADR-0007)。 -/ vpath : VPath diff --git a/spec/Spec/Courseware/Open.lean b/spec/Spec/Courseware/Open.lean index d5f8fcb..620c6aa 100644 --- a/spec/Spec/Courseware/Open.lean +++ b/spec/Spec/Courseware/Open.lean @@ -2,8 +2,8 @@ import Spec.Courseware.Open.QuestionBank import Spec.Courseware.Open.Course /-! -# Courseware.Open —— 留白骨架(核心关系 OPEN) +# Courseware.Open —— OPEN 骨架(核心关系) 题库与 element 的关系(`QuestionBank`)、课程编排规则(`Course`)。两者均为已 surface -但未决策的分歧点,按宪法第 2 条不臆造,待专门 ADR 落定。 +但未决策的 OPEN 分歧点,待专门 ADR 落定。 -/ diff --git a/spec/Spec/Courseware/Open/Course.lean b/spec/Spec/Courseware/Open/Course.lean index 50617a4..aeef406 100644 --- a/spec/Spec/Courseware/Open/Course.lean +++ b/spec/Spec/Courseware/Open/Course.lean @@ -5,6 +5,6 @@ ADR-0005:工程文件的粒度是**单节课**;course / 单元**不是**工程 **编排**。但"编排"的具体规则未决策:有序列表还是带层级(单元 → 课)的树?lesson 被 引用还是被包含?跨 lesson 有无约束(目标覆盖、前后置)?这些都是 `OPEN`。 -按宪法第 2 条本模块**不臆造**编排结构——不建 `Course := List Lesson`(那会偷偷承诺 +此处不替它选解——不建 `Course := List Lesson`(那会偷偷承诺 "扁平有序、无层级")。只在此 surface:课程编排待专门 ADR。本文件当前不引入任何承诺性声明。 -/ diff --git a/spec/Spec/Courseware/Open/QuestionBank.lean b/spec/Spec/Courseware/Open/QuestionBank.lean index 7270d8d..57d629c 100644 --- a/spec/Spec/Courseware/Open/QuestionBank.lean +++ b/spec/Spec/Courseware/Open/QuestionBank.lean @@ -5,6 +5,6 @@ 典型的可复用单元,lesson 会引用它。但**题库与 element 的关系尚未决策**,且用户明确 指出"纯引用可能不够"——element 内联题目数据 / lesson 持指向题库条目的引用 / 两者并存? -这是一个 `OPEN` 分歧点。按宪法第 2 条本模块**不替它选解**——不建 `QuestionRef` 也不建 +这是一个 `OPEN` 分歧点。此处不替它选解——不建 `QuestionRef` 也不建 内联结构,只在此 surface。待专门 ADR 落定后再填。本文件当前不引入任何承诺性声明。 -/ diff --git a/spec/Spec/Prelude.lean b/spec/Spec/Prelude.lean index 4439d4d..bd92fbe 100644 --- a/spec/Spec/Prelude.lean +++ b/spec/Spec/Prelude.lean @@ -1,10 +1,10 @@ /-! # Prelude —— System 层共享标识符 -平台层反复引用一组标识符(项目、run、session、principal、chat、platform identity/audit)。其内部表示从未被决策 -(UUID / 复合键、principal 子类型学),也非分歧点,故收口成 opaque 载体 -`Identifiers`,System 各模块在其上参数化——契约谈得了"锁 owner 是哪个 run"这类 -**关系**,却不对标识符表示作承诺。 +平台层引用一组标识符(项目、run、session、principal、chat、platform identity/audit)。 +其内部表示从未被决策(UUID / 复合键、principal 子类型学),也非分歧点,故收口成 opaque +载体 `Identifiers`,System 各模块在其上参数化——契约谈得了"锁 owner 是哪个 run"这类 +关系,却不对标识符表示作承诺。 -/ namespace Spec.System @@ -37,11 +37,11 @@ structure Identifiers where 记忆/锚点重建,不由 session 自带。同 provider/model 内可跨多 run 复用, ADR-0002)。 -/ SessionId : Type /-- 权限主体标识(`OPEN` 表示及其子类型学;ADR-0004 的 user/chat/department/… - 子类型学未定且非本层分歧点,纯 plumbing,故只留 opaque 键)。 -/ + 子类型学未定且非分歧点,故只留 opaque 键)。 -/ Principal : Type /-- 飞书项目群 chat 标识(`OPEN` 表示;ADR-0001 协作空间、ADR-0003 锚点引用、 ADR-0004 `feishu_chat` principal 三处共用同一实体。独立成载体而非 `Principal` 子 - 类型——principal 子类型学 OPEN 见上,本层不预设"chat 是 principal 的哪种子型")。 -/ + 类型——principal 子类型学 OPEN,这里不预设"chat 是 principal 的哪种子型")。 -/ ChatId : Type /-- 平台管理员身份标识(`OPEN` 表示;ADR-0023,不复用客户 `User` 标识)。 -/ PlatformIdentityId : Type diff --git a/spec/Spec/System.lean b/spec/Spec/System.lean index 0b9796c..fc8695a 100644 --- a/spec/Spec/System.lean +++ b/spec/Spec/System.lean @@ -17,9 +17,8 @@ import Spec.System.Audit /-! # System —— Hub 平台层契约 -协作与执行的平台:项目、飞书群、AgentRun、锁、权限、审计、按需上下文。likec4 -(`docs/architecture/likec4/`)已画出这一层的**结构**;本层只补 likec4 画不出的 -**语义分歧点**: +协作与执行的平台:项目、飞书群、AgentRun、锁、权限、审计、按需上下文。 +likec4 已画出结构;这里补语义: - `Hierarchy` —— 三层主体:平台 → 组织 → 用户。 - `User` —— 用户创建路径(管理员直接创建;飞书注册 `OPEN`)。 @@ -37,14 +36,14 @@ import Spec.System.Audit - `AgentRole` —— org-scoped agent 角色配置 + 技能(ADR-0017/0018)。 - `Run` —— AgentRun 状态与终止判定(状态集合完整性 OPEN)。 - `Lock` —— 锁 owner=run(ADR-0002),及"持锁者必为非终止 run"的核心不变式。 -- `Memory` —— 按需上下文:锚点类别(ADR-0003)+ MCP 工具按 run/project 上下文授权的不变式。 +- `Memory` —— 按需上下文:锚点类别(ADR-0003)+ MCP 工具按 run/project 上下文授权。 - `AgentSurface` —— agent 执行面被 run 的工作区所界定(ADR-0018);与 Lock 正交—— Lock 限定并发,Surface 限定波及面。机制 OPEN。 - `Permission` —— read⊂edit⊂manage 角色体系、能力推导、单调性;force-release 在格外。 -- `PermissionGrant` —— grant(resource×principal×role)与 settings(六 policy 旋钮)结构 - (ADR-0004);role-capability 与 settings-policy 的组合规则 OPEN。 -- `Audit` —— customer Project/Run 审计有意从简(内容多为 plumbing,OPEN);Platform - Audit 由 `PlatformAdministration` 独立钉死。 +- `PermissionGrant` —— grant(resource×principal×role)与 settings(六 policy 旋钮) + (ADR-0004);组合规则 OPEN。 +- `Audit` —— customer Project/Run 审计从简(内容 OPEN);Platform Audit 由 + `PlatformAdministration` 独立承载。 标识符见 `Spec.Prelude`。决策出处:ADR-0001..0004, 0018, 0020..0024。 -/ diff --git a/spec/Spec/System/Agent/AgentSurface.lean b/spec/Spec/System/Agent/AgentSurface.lean index 7d21568..2e4ba9d 100644 --- a/spec/Spec/System/Agent/AgentSurface.lean +++ b/spec/Spec/System/Agent/AgentSurface.lean @@ -4,19 +4,14 @@ import Spec.System.Agent.Run /-! # AgentSurface —— Agent 执行面边界(ADR-0018) -ADR-0001/0002/0004 覆盖"协作治理"直到 `triggerAgent`:谁能触发一次 run。但触发之后 -agent 在执行层面能干什么——读哪些文件、跑什么命令——任何 ADR / 散文都未定。ADR-0017 -落地时采用 Claude Code SDK 的 `bypassPermissions` + 全量 Read/Write/Bash/Glob/Grep, -agent 的文件与 shell 面对宿主**无界**:spec 与 ADR 均未钉死边界。本模块补这一层。 +Agent 在一次 run 内发起的文件操作,其路径必须落在该 run 所属 project 的工作区目录内 +(ADR-0007)。逃逸即越权,拒绝。 -钉死的不变式:agent 在一次 run 内发起的文件操作,其路径必须落在该 run 所属 project -的工作区目录内(ADR-0007:工程文件是目录树;此 ADR 固定"agent 操作落在该树内")。 -逃逸即越权,拒绝。与 `Lock`(ADR-0002)正交:Lock 限定**并发**(谁在改),Surface -限定**波及面**(能改到哪)。二者都按 run × project 作用域。 +与 `Lock`(ADR-0002)正交:Lock 限定并发(谁在改),Surface 限定波及面(能改到哪)。 +二者都按 run × project 作用域。 -shell 面的边界(命令的文件效果同样不得逃逸工作区)是同一不变式的推论,但**机制** -——路径校验工具包装、OS 级沙箱(bubblewrap/容器)、SDK 权限钩子,或其组合——`OPEN` -(ADR-0018)。契约钉死不变式,不钉死机制。 +shell 面的边界(命令的文件效果同样不得逃逸工作区)是同一不变式的推论。机制 +——路径校验、OS 级沙箱、SDK 权限钩子,或其组合——`OPEN`(ADR-0018)。 -/ namespace Spec.System @@ -24,17 +19,18 @@ namespace Spec.System variable (I : Identifiers) (Path : Type) /-- Agent 在一次 run 内发起的文件操作(`PINNED` 关系, ADR-0018)。由某 run 发起、 -指向某路径;是否越权由下方 `Authorized` 钉死。 -/ +指向某路径;是否越权由下方 `Authorized` 约束。 -/ structure AgentFileOp where /-- 发起操作的 run(授权上下文主体, ADR-0018;与 `Lock` 同作用域 run × project)。 -/ run : I.RunId - /-- 操作目标路径(`PINNED` 字段, ADR-0018)。 -/ + /-- 操作目标路径(`PINNED`, ADR-0018)。 -/ path : Path + /-- 工作区边界良构:run 的文件操作路径必须落在该 run 所属 project 的工作区目录内 -(`PINNED` 平台核心安全不变式, ADR-0018)。`runWorkspace` 与 `pathWithin` 均由平台提供 -(表示 `OPEN`——路径如何表示、"在内"如何判定是纯 plumbing,非本层分歧点);本谓词只 -钉死"操作路径必须以 run 的工作区为根",杜绝 agent 越权读写宿主任意文件。 -/ +(`PINNED` 安全不变式, ADR-0018)。`runWorkspace` 与 `pathWithin` 由平台提供 +(表示 `OPEN`);本谓词约束"操作路径必须以 run 的工作区为根",杜绝 agent 越权读写 +宿主任意文件。 -/ def AgentFileOp.Authorized (op : AgentFileOp I Path) (runWorkspace : I.RunId → Option Path) diff --git a/spec/Spec/System/Agent/Memory.lean b/spec/Spec/System/Agent/Memory.lean index e550b09..fa9ec14 100644 --- a/spec/Spec/System/Agent/Memory.lean +++ b/spec/Spec/System/Agent/Memory.lean @@ -3,13 +3,13 @@ import Spec.Prelude /-! # Memory —— 按需上下文:锚点与项目记忆(ADR-0003) -ADR-0003:Hub **只存锚点与项目记忆**,不存全量飞书消息历史;Claude 需要更多上下文时经 -飞书 API 按需读取。本模块刻画所存锚点的**类别**(ADR 列定,枚举完整性 OPEN——ADR 是 -"例如"式列举,新增类别不违反契约),并钉死一条 likec4 画不出的安全不变式:**MCP 工具 -按 run/project 上下文授权,Claude 不得传任意 chat id**(ADR-0003 Consequences 末条)。 +ADR-0003:Hub 只存锚点与项目记忆,不存全量飞书消息历史;Claude 需要更多上下文时经 +飞书 API 按需读取。这里刻画所存锚点的类别(ADR 列定,枚举完整性 OPEN),并约束 +一条安全不变式:MCP 工具按 run/project 上下文授权,Claude 不得传任意 chat id +(ADR-0003 Consequences 末条)。 "chat id 与 project 绑定"这一锚点类别由 `ProjectGroup.GroupBinding`(ADR-0001)权威承载, -本模块不重复声明,只覆盖其余飞书侧指针(触发消息、状态卡片、回复、线程)。 +这里只覆盖其余飞书侧指针(触发消息、状态卡片、回复、线程)。 -/ namespace Spec.System @@ -17,9 +17,8 @@ namespace Spec.System variable (I : Identifiers) variable (MessageId CardId : Type) -/-- 上下文锚点(`PINNED` 类别, ADR-0003 列定;**枚举完整性 `OPEN`**——ADR 是"例如"式 -列举,实现若需新类别须 surface,不得默认本枚举已穷尽)。承载 Hub 保留的飞书侧最小指针, -而非消息正文。 -/ +/-- 上下文锚点(`PINNED` 类别, ADR-0003;枚举完整性 `OPEN`——ADR 是"例如"式列举, +实现若需新类别须 surface)。承载 Hub 保留的飞书侧最小指针,而非消息正文。 -/ inductive Anchor where /-- 触发某次 run 的消息(`PINNED` 类别, ADR-0003 "trigger message id")。 -/ | triggerMessage : MessageId → Anchor @@ -35,13 +34,11 @@ MCP tools to read … through Feishu APIs")。 -/ structure McpReadRequest where /-- 发起请求的 run(授权上下文主体, ADR-0003)。 -/ run : I.RunId - /-- 请求读取的 chat(是否允许越界由下方 `Authorized` 钉死:不允许)。 -/ + /-- 请求读取的 chat(授权由下方 `Authorized` 约束:不允许越界)。 -/ chat : I.ChatId /-- 请求获授权:其 chat 必须等于该 run 所属 project 的绑定群(`PINNED` 安全不变式, -ADR-0003 Consequences "MCP tools must authorize by run/project context; Claude cannot -pass arbitrary chat ids")。`runProject`/`boundChat` 由平台提供(表示 `OPEN`);本谓词只 -钉死"chat 必须匹配 run 的 project 绑定",杜绝 Claude 传任意 chat id 越权读取。 -/ +ADR-0003)。"chat 必须匹配 run 的 project 绑定",杜绝 Claude 传任意 chat id。 -/ def McpReadRequest.Authorized (req : McpReadRequest I) (runProject : I.RunId → Option I.ProjectId) diff --git a/spec/Spec/System/Agent/Run.lean b/spec/Spec/System/Agent/Run.lean index 5bcb865..46bcb12 100644 --- a/spec/Spec/System/Agent/Run.lean +++ b/spec/Spec/System/Agent/Run.lean @@ -2,15 +2,15 @@ # Run —— AgentRun 状态机 一次 `@bot` 创建一个 `AgentRun`(ADR-0001),它在终止时释放项目锁(ADR-0002)。 -合法转移关系在任何 ADR / likec4 散文里都未定下,故本模块只刻画**状态**与**终止 -判定**(后者是 Lock 排他不变式的依赖),不臆造转移边。 +转移关系在任何 ADR 里都未定,这里只刻画状态与终止判定(后者是 Lock 排他不变式的 +依赖),不定义转移边。 -/ namespace Spec.System -/-- AgentRun 运行状态(状态名 `PINNED`, ADR-0001..0003, ADR-0022 + likec4; -**完整性 `OPEN`**——散文从未声明"状态恰好这些";实现若需新状态(如 pending)须 -surface,不得默认本枚举已穷尽)。终止态见 `RunState.Terminal`。 -/ +/-- AgentRun 运行状态(状态名 `PINNED`, ADR-0001..0003, ADR-0022;完整性 `OPEN` +——ADR 从未声明"状态就是这些";实现若需新状态(如 pending)须 surface)。终止态 +见 `RunState.Terminal`。 -/ inductive RunState where | active | waitingForUser diff --git a/spec/Spec/System/Audit.lean b/spec/Spec/System/Audit.lean index 7eee96e..54f2dcf 100644 --- a/spec/Spec/System/Audit.lean +++ b/spec/Spec/System/Audit.lean @@ -1,21 +1,18 @@ import Spec.Prelude /-! -# Audit —— Project/Run 审计日志(有意从简) +# Audit —— Project/Run 审计日志 -likec4 把 `AuditLog` 列为实体(`AgentRun -> AuditLog 'records lifecycle events'`), -但**审计记录里装什么**(事件 schema、保留策略、可查询维度)在任何 ADR / 散文里都 -未决策,且大多是 plumbing——按分歧点测试不入契约。故本模块刻意几乎为空:只固定 -"审计以 run 为主体记录其生命周期事件"这一条已决策关系,其余 `OPEN`(留白本身是 -契约的一部分:承诺此处尚无答案、勿填)。 +审计记录里装什么(事件 schema、保留策略、可查询维度)在任何 ADR 里都未决策, +且大多是实现细节。这里只固定"审计以 run 为主体记录其生命周期事件"这一条已决策 +关系,其余 `OPEN`。ADR-0023 的 Platform Audit 是另一个控制面,见 +`Spec.System.PlatformAdministration`,不复用本结构。 -/ namespace Spec.System /-- 审计条目的最小骨架(关系 `PINNED` / 内容 `OPEN`, likec4)。只承诺"一条审计记录 -关联到某个 run";事件类型、时间、actor、详情等字段 `OPEN`,待真实分歧点出现时由 -对应 ADR 落定。ADR-0023 的 Platform Audit 是另一个 fail-closed 控制面,见 -`Spec.System.PlatformAdministration`,不复用本结构。 -/ +关联到某个 run";事件类型、时间、actor、详情等字段 `OPEN`。 -/ structure AuditEntry (I : Identifiers) where /-- 该审计条目所属的 run(`PINNED` 关系, likec4)。 -/ run : I.RunId diff --git a/spec/Spec/System/Capacity.lean b/spec/Spec/System/Capacity.lean index 065cd5d..0e3452d 100644 --- a/spec/Spec/System/Capacity.lean +++ b/spec/Spec/System/Capacity.lean @@ -4,8 +4,8 @@ import Spec.Prelude # Capacity —— SaaS capacity admission and abuse controls (ADR-0022) 初始生产服务共享有限的单机资源,但不能让一个 Organization 垄断容量或让无界输入拖垮 -其他租户。ADR-0022 钉死分层限制、持久 admission、显式背压和紧急制动的领域语义; -具体数值必须由生产式容量测试校准,因此保持 `OPEN`,不得把未经验证的数字冒充契约。 +其他租户。ADR-0022 定义分层限制、持久 admission、显式背压和紧急制动;具体数值由 +生产式容量测试校准,保持 `OPEN`。 -/ namespace Spec.System diff --git a/spec/Spec/System/Lock.lean b/spec/Spec/System/Lock.lean index 6ddd4cd..a24afea 100644 --- a/spec/Spec/System/Lock.lean +++ b/spec/Spec/System/Lock.lean @@ -4,32 +4,29 @@ import Spec.System.Agent.Run /-! # Lock —— 项目锁与排他不变式 -ADR-0002 的核心:防止并发 Claude 同改一个项目,锁的 **owner 是当前 `AgentRun`** -(不是 teacher / chat / session)。本模块把这条决策编码进类型,并钉死那条 -likec4 画不出的语义不变式——**持锁者必为非终止 run**。 +ADR-0002:防止并发 agent 同改一个项目,锁的 owner 是当前 `AgentRun`(不是 +teacher / chat / session)。持锁者必为非终止 run。 -/ namespace Spec.System variable (I : Identifiers) -/-- 项目级锁(`PINNED`, ADR-0002)。`owner : RunId`(非 SessionId/Principal)从类型 -上编码"lock owner = run_id":锁不可能被 session / teacher 持有。 -/ +/-- 项目级锁(`PINNED`, ADR-0002)。`owner : RunId`从类型上编码"lock owner = run_id": +锁不可能被 session / teacher 持有。 -/ structure ProjectAgentLock where - /-- 作用域:项目级(`PINNED`, ADR-0002 `scope = project_id`)。 -/ + /-- 作用域:项目级(`PINNED`, ADR-0002)。 -/ scope : I.ProjectId - /-- 持有者:一个 run(`PINNED`, ADR-0002 `owner = run_id`)。 -/ + /-- 持有者:一个 run(`PINNED`, ADR-0002)。 -/ owner : I.RunId /-- 锁表:每项目当前持锁 run(`PINNED` 排他性, ADR-0002)。`ProjectId → Option RunId` -的结构**本身**即排他——不可能为同一项目登记两个并发 owner。 -/ +的结构本身即排他——不可能为同一项目登记两个并发 owner。 -/ def LockTable := I.ProjectId → Option I.RunId -/-- 锁表良构:**持锁者必为非终止 run**(`PINNED` 平台核心不变式, ADR-0002)。 +/-- 锁表良构:持锁者必为非终止 run(`PINNED`, ADR-0002)。 -"锁在 run 终止时释放"的逻辑等价物:若 `p` 的锁被 `r` 持有,则 `r` 不在终止态。这条 -把 Lock 与 Run 耦合起来——likec4 能画"run owns lock while running",画不出"终止即 -必须释放"这个约束;它正是契约相对结构图的增量。 -/ +若 `p` 的锁被 `r` 持有,则 `r` 不在终止态。锁在 run 终止时释放。 -/ def LockTable.WellFormed (lt : LockTable I) (statusOf : I.RunId → RunState) : Prop := ∀ p r, lt p = some r → ¬ (statusOf r).Terminal diff --git a/spec/Spec/System/Organization.lean b/spec/Spec/System/Organization.lean index 8f29e8d..372fd1f 100644 --- a/spec/Spec/System/Organization.lean +++ b/spec/Spec/System/Organization.lean @@ -35,10 +35,9 @@ structure TeamProjectGrantScope where project : I.ProjectId /-- 获得授权的 team principal(`PINNED`, ADR-0020)。 -/ team : I.TeamId - /-- Team-project grant 是良构的 iff project 与 team 解析到同一 organization -(`PINNED`, ADR-0020)。`projectOrg`/`teamOrg` 由平台提供(表示 `OPEN`);本谓词钉死 -跨 org team grant 必须被拒绝。 -/ +(`PINNED`, ADR-0020)。`projectOrg`/`teamOrg` 由平台提供(表示 `OPEN`);跨 org +team grant 必须被拒绝。 -/ def TeamProjectGrantScope.WellScoped (grant : TeamProjectGrantScope I) (projectOrg : I.ProjectId → Option I.OrganizationId) @@ -71,7 +70,7 @@ inductive OrganizationConnectionStatus where /-- Organization secret version 的信封绑定上下文(`PINNED`, ADR-0024):认证附加数据必须 同时绑定 organization、connection、secret version 与 purpose,因此密文不能跨行、跨 org、 -跨 connection 或跨用途替换。各标识符的数据库表示属于 plumbing,这里保持 opaque。 -/ +跨 connection 或跨用途替换。各标识符的数据库表示为实现细节,这里保持 opaque。 -/ structure OrganizationSecretBinding (OrganizationId ConnectionId SecretVersionId Purpose : Type) where /-- secret 所属 organization(`PINNED`, ADR-0024)。 -/ diff --git a/spec/Spec/System/Permission.lean b/spec/Spec/System/Permission.lean index 8fcc721..4c3fb9e 100644 --- a/spec/Spec/System/Permission.lean +++ b/spec/Spec/System/Permission.lean @@ -5,8 +5,7 @@ import Spec.Prelude ADR-0004:权限走"飞书云文档式"——grant(`resource + principal + role`)与 settings 分离;role 取自封闭的 `read / edit / manage`,且 **read ⊂ edit ⊂ manage** 累积赋能; -强制释放锁是 **admin-only**,在 role 体系之外。本模块把这套结构与"高 role 含低 -role 全部能力"的单调性钉死。 +强制释放锁是 **admin-only**,在 role 体系之外。 -/ namespace Spec.System diff --git a/spec/Spec/System/PermissionGrant.lean b/spec/Spec/System/PermissionGrant.lean index 6718180..30d607b 100644 --- a/spec/Spec/System/PermissionGrant.lean +++ b/spec/Spec/System/PermissionGrant.lean @@ -4,16 +4,14 @@ import Spec.System.Permission /-! # PermissionGrant —— 授权与设置(ADR-0004) -ADR-0004 的"飞书云文档式"权限:**grant**(`resource × principal × role`)与 **settings** -(各 policy 旋钮)分离;role 决定"谁能"(能力,见 `Permission`),settings 决定"此资源 -是否开某类操作"(策略)。本模块把 grant/settings 的结构钉死——`Permission` 已落 role 能 -力格,本模块补"授权如何挂到资源/主体上"。 +ADR-0004 的"飞书云文档式"权限:grant(`resource × principal × role`)与 settings +(各 policy 旋钮)分离;role 决定能力(见 `Permission`),settings 决定"此资源是否 +开某类操作"。 -principal 子类型学(user/chat/department/…)与各 policy 值域均为 `OPEN`(ADR 未定,非本 -层分歧点)。**role-capability 与 settings-policy 如何组合成最终授权决策**亦 `OPEN`—— -ADR-0004 把二者列为分离的闸,但未明文规定组合规则(AND?settings 能否超出 role?),实现 -须 surface,不得默认。ADR-0020 另行钉死 TEAM principal 授权 PROJECT resource 时必须 -同 organization;该 tenant well-scopedness 见 `Spec.System.Organization`。 +principal 子类型学(user/chat/department/…)与各 policy 值域 `OPEN`。role-capability +与 settings-policy 如何组合成最终授权决策亦 `OPEN`——ADR-0004 把二者列为分离的闸, +但未明文规定组合规则。ADR-0020 约束 TEAM principal 授权 PROJECT resource 时必须同 +organization,见 `Spec.System.Organization`。 -/ namespace Spec.System @@ -45,9 +43,8 @@ structure PermissionGrant where role : Role /-- 资源策略设置(`PINNED` 结构 + 六旋钮, ADR-0004 `PermissionSettings`):与 grant 分离, -控制"此资源是否开某类操作"。六旋钮由 ADR 逐字列名;各旋钮值域 `OPEN`(ADR 未定,非本层 -分歧点)。共享同一 opaque `Policy` 类型:契约只钉死"旋钮存在且相互独立",不钉死"各旋钮 -值域互异"——值域是实现/后续 ADR 的事。 -/ +控制"此资源是否开某类操作"。六旋钮由 ADR 逐字列名;各旋钮值域 `OPEN`(ADR 未定)。 +共享同一 opaque `Policy` 类型:契约只约束"旋钮存在且相互独立";值域是实现/后续 ADR 的事。 -/ structure PermissionSettings where /-- 设置所属资源(`PINNED`, ADR-0004)。 -/ resource : Resource I ArtifactId diff --git a/spec/Spec/System/PlatformAdministration.lean b/spec/Spec/System/PlatformAdministration.lean index 8396ca5..92f83d6 100644 --- a/spec/Spec/System/PlatformAdministration.lean +++ b/spec/Spec/System/PlatformAdministration.lean @@ -8,8 +8,8 @@ import Spec.Prelude 单一平级管理员角色、绑定身份的 invitation、可撤销服务端 session、mutation 与平台审计 同成同败、最后管理员保护,以及无常驻账号的双因子离线恢复。 -本模块只钉死这些会导致安全边界分歧的语义。cookie 属性、token hash、具体 TTL、审计 -字段表示/保留期、recovery key 介质和 CLI/SQL 机制仍为 `OPEN`,由对应实现决策承载。 +这些安全边界语义见下。cookie 属性、token hash、具体 TTL、审计 +字段表示/保留期、recovery key 介质和 CLI/SQL 机制 `OPEN`。 -/ namespace Spec.System diff --git a/spec/Spec/System/ProjectGroup.lean b/spec/Spec/System/ProjectGroup.lean index cb3cfdb..b5cba44 100644 --- a/spec/Spec/System/ProjectGroup.lean +++ b/spec/Spec/System/ProjectGroup.lean @@ -3,26 +3,23 @@ import Spec.Prelude /-! # ProjectGroup —— 飞书项目群作为协作空间(ADR-0001) -ADR-0001 的核心:一个 project 对应一个**长生命周期**飞书项目群;群是协作空间,**不是锁 -owner**(锁归 `AgentRun`,见 `Lock` / ADR-0002),不是临时处理 session。群可在无 Claude -处理时保持开启;教师离群/静音与项目权限、与 Claude 生命周期相互独立。本模块钉死 -project↔group 的**active**一对一绑定——likec4 画得出"project has group",画不出"恰好一个、 -且群不持锁"。 +一个 project 对应一个长生命周期飞书项目群;群是协作空间,不持锁(锁归 `AgentRun`, +见 `Lock` / ADR-0002)。群可在无 agent 处理时保持开启;教师离群/静音与项目权限、 +与 agent 生命周期相互独立。 -**绑定历史(`PINNED`, ADR-0021):** active binding 严格 1:1;实现可以保留 archived -historical binding rows 供审计/纠错,但 `GroupBinding` 谓词只刻画当前 active 快照。 -群解散/不可达的自动化处理仍为 `OPEN`;pilot 纠错由 org admin 显式归档绑定。 +**绑定历史(`PINNED`, ADR-0021):** active binding 严格 1:1;实现可保留 archived +historical binding rows 供审计,但 `GroupBinding` 谓词只刻画当前 active 快照。 +群解散/不可达的自动化处理 `OPEN`;pilot 纠错由 org admin 显式归档绑定。 -/ namespace Spec.System variable (I : Identifiers) -/-- 飞书项目群(`PINNED` 长生命周期协作空间, ADR-0001)。承载 project 与飞书 chat 的绑定; -**不是锁 owner**(锁归 `AgentRun`,见 `Lock`);不是临时 session。 -/ +/-- 飞书项目群(`PINNED` 长生命周期协作空间, ADR-0001)。承载 project 与飞书 chat 的 +绑定;不持锁(锁归 `AgentRun`,见 `Lock`)。 -/ structure ProjectGroup where - /-- 群对应的飞书 chat(`PINNED` 关系, ADR-0001 "one project has one Feishu project - group";chat 标识见 `Identifiers.ChatId`)。 -/ + /-- 群对应的飞书 chat(`PINNED` 关系, ADR-0001;chat 标识见 `Identifiers.ChatId`)。 -/ chat : I.ChatId /-- 项目↔active 群绑定表(`PINNED` 每项目至多一个 active 群, ADR-0001/0021)。 @@ -30,9 +27,9 @@ structure ProjectGroup where 自带);良构补另一半——单射。 -/ def GroupBinding := I.ProjectId → Option I.ChatId -/-- Active 绑定良构:**单射**——不同 project 不绑同一 active chat(`PINNED` 1:1 的另一半, -ADR-0001/0021)。"每 project 至多一个群"由 `Option` 结构自带;这条钉死"每群至多属于一个 -project"。archived historical bindings 不在本快照不变式内。 -/ +/-- Active 绑定良构:单射——不同 project 不绑同一 active chat(`PINNED`, ADR-0001/0021)。 +"每 project 至多一个群"由 `Option` 结构自带;这条约束"每群至多属于一个 project"。 +archived historical bindings 不在本快照不变式内。 -/ def GroupBinding.WellFormed (b : GroupBinding I) : Prop := ∀ p₁ p₂ c, b p₁ = some c → b p₂ = some c → p₁ = p₂ diff --git a/spec/Spec/System/ProjectWorkspace.lean b/spec/Spec/System/ProjectWorkspace.lean index 8b672ec..1edd89f 100644 --- a/spec/Spec/System/ProjectWorkspace.lean +++ b/spec/Spec/System/ProjectWorkspace.lean @@ -3,12 +3,11 @@ import Spec.Prelude /-! # ProjectWorkspace —— project explorer 与飞书建项入口(ADR-0021) -ADR-0021 把 org 后台里的"文件管理器式"项目管理收口为透明 folder + project: -folder 只负责导航、排序、层级与用量聚合,当前不是权限资源。project 仍是授权边界。 +org 后台里的"文件管理器式"项目管理:folder 只负责导航、排序、层级与用量聚合,当前 +不是权限资源。project 仍是授权边界。 -本模块只钉死会影响实现分歧的不变量:folder/project 同 org、folder 不参与权限、普通成员 -从飞书群创建 project 必须受 org policy 控制。folder visibility/team policy/继承授权仍为 -未来扩展,不得在当前实现中半隐式加入。 +不变量:folder/project 同 org、folder 不参与权限、普通成员从飞书群创建 project 受 +org policy 控制。folder visibility/team policy/继承授权 `OPEN`。 -/ namespace Spec.System @@ -39,7 +38,7 @@ def ProjectFolderPlacement.WellScoped ∃ o, projectOrg placement.project = some o ∧ folderOrg placement.folder = some o /-- Folder 当前透明(`PINNED`, ADR-0021):folder 不是权限资源,不持有 grants,移动 project -不改变 project 自身授权。未来 folder policy 若出现,必须新增显式语义而不是复用本谓词。 -/ +不改变 project 自身授权。未来 folder policy 若出现,须新增显式语义。 -/ structure FolderTransparent where /-- 透明性命题本身;字段存在是为了让 contract 明确可引用(`PINNED`, ADR-0021)。 -/ current : True diff --git a/spec/Spec/System/User.lean b/spec/Spec/System/User.lean index d04b11a..c1f2aa7 100644 --- a/spec/Spec/System/User.lean +++ b/spec/Spec/System/User.lean @@ -5,7 +5,7 @@ import Spec.Prelude 用户实体见 `Hierarchy.User`;外部连接见 `Spec.System.Connections`。 -用户创建当前只钉管理员直接创建;飞书自助注册→管理员审批未钉死(`OPEN`)。 +用户创建当前只定义管理员直接创建;飞书自助注册→管理员审批 `OPEN`。 -/ namespace Spec.System