chore: add c4 spec

This commit is contained in:
2026-06-06 19:22:58 +08:00
commit ca9f1242f8
11 changed files with 2490 additions and 0 deletions
+126
View File
@@ -0,0 +1,126 @@
model {
teacher = actor 'Teacher' {
description '教研老师。可以同时参与多个项目群,在群里讨论并在需要时 @Claude。'
}
admin = actor 'Admin' {
description '管理员。管理项目、权限、异常锁释放和审计。'
}
feishu = externalSystem 'Feishu / Lark' {
description '项目群、消息、卡片交互和历史消息 API。'
}
claude = externalSystem 'Claude / Claude Code' {
description '执行课程资料生成、修改和问答的 AI agent。'
}
workspace = externalSystem 'Project Workspace' {
description '每个课程项目的文件、素材、课件、教案和生成产物所在工作区。'
}
hub = system 'Curriculum Project Hub' {
description '教研项目工作台。把项目、飞书群、Claude run、锁、上下文和产物管理成一个明确的协作系统。'
web = app 'Project Console' {
description '后台/工作台 UI,用于项目管理、权限、run 审计、异常取消和强制释放锁。'
}
api = service 'Hub API' {
description '对 UI、飞书回调、内部 agent runtime 暴露业务 API。'
}
project = service 'Project Service' {
description '管理 Project、成员、项目群绑定、项目状态和产物引用。'
}
permission = service 'Permission Service' {
description '飞书云文档式权限层。按 resource + principal + role 授权,并用 permission settings 控制分享、评论、下载、协作者管理和 Claude 调用。'
}
im = adapter 'IM Bridge Adapter' {
description '飞书/Lark 适配层。负责事件去重、卡片、消息投递、按钮回调和限流;优先评估复用 Claude-to-IM 的 adapter/delivery 能力。'
}
run = service 'Run Orchestrator' {
description '在 @Claude 时创建 AgentRun,申请项目锁,调度 Claude runtime,处理取消、失败、等待用户和完成。'
}
locks = service 'Project Lock Service' {
description '统一项目级锁。scope = project_idowner = run_idlifetime = 一次 Claude run。'
}
context = service 'Context Service' {
description '提供当前触发消息、回复链、项目记忆、产物上下文和按需历史读取。第一版不长期缓存飞书全文。'
}
mcp = service 'MCP Gateway' {
description 'Claude 可调用的内部工具入口,例如读取当前项目群历史、触发消息、附件和产物上下文。'
}
runtime = runtime 'Claude Runtime' {
description '调用 Claude Code / SDK / wrapper,维护 provider session resume,并执行具体任务。'
}
db = store 'Application Database' {
description '项目、群绑定、AgentSession、AgentRun、锁、审计和轻量索引。'
projectEntity = entity 'Project'
groupEntity = entity 'ProjectGroupChat'
sessionEntity = entity 'AgentSession'
runEntity = entity 'AgentRun'
lockEntity = entity 'ProjectAgentLock'
permissionEntity = entity 'PermissionGrant'
settingsEntity = entity 'PermissionSettings'
memoryEntity = entity 'ProjectMemory / Decisions'
auditEntity = entity 'AuditLog'
}
web -> api 'manage projects, runs and locks'
api -> project 'project commands'
api -> permission 'authorize user actions'
api -> run 'trigger/cancel/admin actions'
api -> db 'read/write application state'
im -> api 'Feishu events and card callbacks'
im -> feishu 'send messages, cards, updates'
im -> project 'bind chat to project'
im -> permission 'check card/action permissions'
im -> run 'create/cancel run on @Claude or card action'
project -> db 'persist projects and group bindings'
permission -> db 'persist grants and settings'
run -> locks 'acquire/release project lock'
run -> permission 'authorize trigger/cancel/force release'
locks -> db 'persist lock owner = run_id'
run -> context 'build seed context'
run -> runtime 'execute Claude turn'
run -> db 'persist AgentRun state'
runtime -> claude 'resume/call Claude'
runtime -> workspace 'read/write project files'
runtime -> mcp 'request project context'
mcp -> context 'resolve run-scoped context'
context -> feishu 'read messages by chat/thread/message id'
context -> db 'read project memory and anchors'
projectEntity -> groupEntity 'has project group'
projectEntity -> permissionEntity 'has collaborators'
projectEntity -> settingsEntity 'has operation settings'
projectEntity -> sessionEntity 'has long-lived Claude session'
projectEntity -> runEntity 'has many runs'
projectEntity -> lockEntity 'has current lock'
permissionEntity -> settingsEntity 'is constrained by'
runEntity -> lockEntity 'owns while running'
sessionEntity -> runEntity 'is reused by runs'
projectEntity -> memoryEntity 'stores summary and decisions'
runEntity -> auditEntity 'records lifecycle events'
}
teacher -> feishu 'discusses in project groups'
teacher -> hub.im '@Claude and card actions'
teacher -> hub.web 'reviews projects and runs'
admin -> hub.web 'administers locks, permissions and audit'
feishu -> hub.im 'message events and callbacks'
}
+80
View File
@@ -0,0 +1,80 @@
specification {
element actor {
style {
shape person
color indigo
}
}
element system {
style {
color primary
}
}
element externalSystem {
style {
color gray
}
}
element app {
style {
shape browser
color green
}
}
element service {
style {
shape component
color primary
}
}
element adapter {
style {
shape component
color amber
}
}
element runtime {
style {
shape component
color secondary
}
}
element store {
style {
shape storage
color muted
}
}
element entity {
style {
shape storage
color muted
}
}
relationship sync {
line solid
}
relationship async {
line dotted
}
relationship reads {
line dashed
color gray
}
relationship writes {
line solid
color gray
}
}
+103
View File
@@ -0,0 +1,103 @@
views {
view index {
title 'System Context'
description '''
Curriculum Project Hub treats a Feishu group as the long-lived project collaboration space.
Claude joins only when teachers @Claude, and the Hub creates one AgentRun for that work.
'''
include teacher, admin, hub, feishu, claude, workspace
}
view containers of hub {
title 'Target Containers'
description 'Main runtime containers and external integrations for the target architecture.'
include *
include teacher, admin, feishu, claude, workspace
}
view permissions of hub {
title 'Permission Model'
description '''
Permissions follow the Feishu Docs pattern: collaborator grants are separate from operation settings.
Grants are resource + principal + role; settings decide who may share, comment, copy/download, manage collaborators and invoke Claude.
'''
include permission, project, im, run, db
include db.projectEntity, db.groupEntity, db.permissionEntity, db.settingsEntity, db.auditEntity
include teacher, admin, feishu
}
view domain_model of hub.db {
title 'Core Domain Model'
description '''
The main correction from the old demo model: project groups and Claude sessions are long-lived,
but project locks are owned by AgentRun, not by teacher, chat or session.
'''
include *
}
dynamic view run_lifecycle {
title '@Claude Run Lifecycle'
description 'A teacher @Claude message creates one AgentRun, which owns the project lock until completion, cancellation or timeout.'
teacher -> feishu 'send @Claude in project group'
feishu -> hub.im 'message event'
hub.im -> hub.project 'resolve chat binding'
hub.im -> hub.permission 'check trigger permission'
hub.im -> hub.run 'create AgentRun'
hub.run -> hub.locks 'acquire project lock with run_id'
hub.run -> hub.context 'build seed context'
hub.run -> hub.runtime 'execute Claude turn'
hub.runtime -> claude 'resume/call Claude'
hub.runtime -> workspace 'read/write project files'
hub.runtime -> hub.mcp 'request extra context if needed'
hub.mcp -> hub.context 'resolve run-scoped tools'
hub.context -> feishu 'read history by message/thread/chat'
hub.runtime -> hub.run 'return result'
hub.run -> hub.im 'publish status/result card'
hub.im -> feishu 'send/update card'
hub.run -> hub.locks 'release run-owned lock'
include teacher, feishu, claude, workspace
}
dynamic view cancel_lifecycle {
title 'Cancel Current Processing'
description 'A permitted teacher can cancel the current active run from card action or @Claude cancel command.'
teacher -> feishu 'click cancel or @Claude cancel'
feishu -> hub.im 'callback/event'
hub.im -> hub.project 'resolve project group binding'
hub.im -> hub.permission 'check cancel permission'
hub.im -> hub.run 'cancel active run'
hub.run -> hub.runtime 'abort controller if local process is alive'
hub.run -> hub.locks 'release lock by run_id'
hub.run -> hub.db 'mark AgentRun canceled and audit actor'
hub.run -> hub.im 'update status card'
hub.im -> feishu 'processing canceled'
include teacher, feishu
}
dynamic view context_access {
title 'On-demand Context Access'
description '''
The Hub stores anchors and memory, not full Feishu history. Claude can call MCP tools
to fetch the current trigger message, reply chain, thread history and recent group messages.
'''
hub.runtime -> hub.mcp 'call get_current_message / get_thread_context'
hub.mcp -> hub.context 'authorize by run_id and project_id'
hub.context -> hub.db 'read anchors, memory and decisions'
hub.context -> feishu 'fetch message/thread/chat history'
feishu -> hub.context 'history page'
hub.context -> hub.mcp 'filtered context'
hub.mcp -> hub.runtime 'tool result'
hub.runtime -> claude 'continue with retrieved context'
include feishu, claude
}
}