import Spec.Prelude

/-!
# ProjectGroup —— 飞书项目群作为协作空间(ADR-0001)

ADR-0001 的核心:一个 project 对应一个**长生命周期**飞书项目群;群是协作空间,**不是锁
owner**(锁归 `AgentRun`,见 `Lock` / ADR-0002),不是临时处理 session。群可在无 Claude
处理时保持开启;教师离群/静音与项目权限、与 Claude 生命周期相互独立。本模块钉死
project↔group 的一对一绑定——likec4 画得出"project has group",画不出"恰好一个、且群
不持锁"。

**群失效态与绑定快照(`OPEN`, ADR-0001 Consequences):** 群解散/归档/不可达时,
`GroupBinding` 快照应变为 `none`(解绑、允许 rebind)还是保持 `some` 但指向失效 chat
(悬挂、待管理员干预)未决策。ADR-0001 已点名 "rebinding or group archival needs
explicit product rules";本模块只刻画健康态 1:1 不变式,不臆造失效态语义。实现遇到
群解散必须 surface,不得默认任一方。
-/

namespace Spec.System

variable (I : Identifiers)

/-- 飞书项目群(`PINNED` 长生命周期协作空间, ADR-0001)。承载 project 与飞书 chat 的绑定;
**不是锁 owner**(锁归 `AgentRun`,见 `Lock`);不是临时 session。 -/
structure ProjectGroup where
  /-- 群对应的飞书 chat(`PINNED` 关系, ADR-0001 "one project has one Feishu project
  group";chat 标识见 `Identifiers.ChatId`)。 -/
  chat : I.ChatId

/-- 项目↔群绑定表(`PINNED` 每项目至多一个群, ADR-0001)。`ProjectId → Option ChatId` 的
结构本身即"一个 project 至多绑一个群"(由 `Option` 自带);良构补另一半——单射。 -/
def GroupBinding := I.ProjectId → Option I.ChatId

/-- 绑定良构:**单射**——不同 project 不绑同一 chat(`PINNED` 1:1 的另一半, ADR-0001)。
"每 project 至多一个群"由 `Option` 结构自带;这条钉死"每群至多属于一个 project"。群
rebinding/archival 是生命周期事件(ADR-0001 Consequences),不在本快照不变式内。 -/
def GroupBinding.WellFormed (b : GroupBinding I) : Prop :=
  ∀ p₁ p₂ c, b p₁ = some c → b p₂ = some c → p₁ = p₂

end Spec.System
