Claudian Collab 前端协作层架构:依赖方向约束、跨表面不变量与元数据交接缓存设计
2026/9/14 19:41:58 网站建设 项目流程

Claudian Collab 前端协作层架构:依赖方向约束、跨表面不变量与元数据交接缓存设计

【免费下载链接】claudianAn Obsidian plugin that embeds Claude Code/Codex as an AI collaborator in your vault项目地址: https://gitcode.com/GitHub_Trending/cl/claudian

本篇技术指南以 Claudian 插件的src/features/collab/协作呈现层架构文档为核心,系统讲解该模块的所有权边界、依赖方向 DAG、一次性读取与持久操作的生命周期分治、跨表面交互不变量,以及CollabPreparedReviewCache元数据交接缓存的实现原理。读完之后,你将掌握如何在 Obsidian 插件的多表面(侧边栏 / 详情 / 模态框)UI 架构中划分关注点、用 latest-task scope 管理可取消的呈现读取、以及如何在表面切换时安全传递有界缓存而不泄露文件内容与凭据。

1. 模块定位:只持有呈现状态与用户意图

架构文档首先声明了src/features/collab/的所有权范围:该目录负责协作(Collab)呈现状态与用户意图,构建在 provider 无关(provider-neutral)的契约之上。同时它被明确禁止导入以下四类实现:

  • 应用层仓库(application repositories);
  • 原生 Git 适配器(Native Git adapters);
  • 权限存储(authority storage);
  • 局域网(LAN)实现与 provider 实现。

这一边界意味着:面板、详情会话、模态框只通过CollabFeaturePort或更窄的注入契约发出操作,自身从不执行 Git 命令,也从不直接修改 Project 记录。CollabFeaturePort定义在 CollabFeaturePort.ts 中,它暴露的不仅是操作方法,还有一套结构化的结果类型。例如CollabResult<T>将结果区分为successcancelledrecovery-requiredstaleconflictfailure六种状态(见 CollabFeaturePort.ts),其中stale携带具体的CollabStaleKind(如project-selectionmainrequest-headworking-copy等),使呈现层可以针对不同的过期原因做出不同 UI 反馈,而不是笼统地显示错误。这种“结果即契约”的设计是呈现层能够与底层 Git/SQL/网络基础解耦的关键:UI 代码只需要消费快照投影(CollabCoordinationSnapshotsource: 'online' | 'cache'stale标志,见 CollabFeaturePort.ts),无需感知底层实现细节。

2. 特征内依赖方向 DAG

文档给出了一张特征内部依赖方向的拓扑图,各呈现表面之间不允许随意互相引用:

composition -> sidebar + detail + modals + handoff + navigation sidebar -> sidebar children + modals + shared + handoff + core detail -> detail children + shared + handoff + core modals -> modal children + shared + core navigation -> injected feature/workspace contracts shared -> Obsidian + core + shared UI/i18n handoff -> core

这条规则有几条明确的负向约束:

  • sharedhandoff不得导入任何呈现表面(sidebar / detail / modals);
  • modals不得导入sidebardetail

结合仓库中的实际目录结构可以验证这一拓扑:sidebar/(含 CollabPanel.ts、changes/tickets/子目录)、detail/(含 CollabDetailView.ts、sessions/review/conflict/)、modals/(含project/下的项目创建、加入、重连、管理等模态框)、navigation/shared/handoff/一一对应。每个表面子目录内部还嵌套了自己的AGENTS.md补充约束,例如 sidebar/AGENTS.md 规定了CollabPanel只拥有项目选择壳与激活状态传播,不拥有数据投影;detail/AGENTS.md 规定了详情会话的状态所有权与 diff 渲染器生命周期。

3. 一次性呈现读取与持久操作的生命周期分治

文档中最具操作性的一条规则区分了两类异步工作:

可丢弃的呈现读取(disposable presentation reads)统一使用“每个逻辑车道一个 latest-task scope”。当同一车道出现新的读取请求时,只有旧读取被失效(invalidated);而Publish、Accept、Ticket/评论变更、冲突解决等持久操作保留应用侧拥有的准入(admission)与幂等意图(idempotency intent),绝不允许放到呈现层的 latest-task scope 之后——否则用户快速点击时旧任务被取消会把已经提交的持久操作“取消掉”。

仓库中的实现印证了这一设计。LatestTaskScope.ts 中的LatestTaskScope类注释即“Owns cancellation and stale-completion fencing for one disposable task lane”(为一个可丢弃任务车道提供取消与过期完成围栏):

  • start()会先cancel()当前任务,再用递增 token +AbortController建立新任务,返回携带signalcomplete()isCurrent()的句柄;
  • complete()只有当任务仍是当前且未被中止时才允许清理,从而防止旧读取的晚到完成覆盖新结果(stale-completion fencing);
  • close()用于控制器销毁时统一中止。

侧边栏文档进一步细化了车道隔离要求:Personal、Team、Ticket 三个面板控制器保持相互独立的读取/取消车道,不得合并成共享刷新任务,更不得用它们的任务 scope 来执行变更操作(见 sidebar/AGENTS.md)。

与之配合的还有 MutationIntentStore.ts:它按 key 为每个变更意图分配mutation{时间戳}_{自增序号}形式的意图 ID,并以JSON.stringify(input)作为身份标识——相同 payload 复用同一意图 ID(幂等),payload 变化则轮换意图 ID,只有被当前 UI 消费的结果才能清除它。这正是“持久操作保留应用拥有的幂等意图”在呈现层的落地:面板替换不能轮换丢失响应的重试,编辑 payload 则必须轮换意图。

4. 交接缓存:CollabPreparedReviewCache 的有界元数据桥

文档指出handoff/CollabPreparedReviewCache.ts是从侧边栏 review 准备详情呈现之间的“有界、插件生命周期、仅元数据”桥:它以持久化身份与精确的 review OID 为键,可以保留协调元数据,但永远不保留文件 blob 或凭据;缺失或不匹配时必须通过注入的 port 重新推导。

CollabPreparedReviewCache.ts 的源码完整印证了这些约束:

const DEFAULT_MAX_ENTRIES = 8; // 有界:最多 8 条 const DEFAULT_TTL_MS = 5 * 60_000; // TTL:5 分钟 export interface CollabPreparedReviewIdentity { readonly comparisonBaseOid: string; readonly comparisonTargetOid: string; readonly projectId: string; readonly requestId: string; readonly reviewedHeadOid: string; readonly reviewedMainOid: string; }

几个值得注意的实现细节:

  • 键的构成identityKeyprojectId:requestId:reviewedMainOid:reviewedHeadOid:comparisonBaseOid:comparisonTargetOid拼接而成(CollabPreparedReviewCache.ts),即“持久化身份 + 精确 OID”双重精确匹配,任何一侧 OID 前进都会导致缓存未命中并触发重新推导;
  • 拒绝不一致输入store()首先校验coordination.snapshot.project.idmainOid是否与 review 声明一致,不一致直接丢弃(CollabPreparedReviewCache.ts),防止跨项目或陈旧 main 的元数据污染;
  • 评论的单调合并:同一键的重复写入不会丢弃已有评论,mergeReviewComments会按评论 ID 去重合并,并取commentCount的最大值,保证评论数只增不减(见 CollabPreparedReviewCache.ts);
  • 有界驱逐与 TTL:写入后按 Map 插入顺序驱逐最旧条目直到不超过maxEntries,读取时若expiresAt <= now()则删除并返回 null(CollabPreparedReviewCache.ts);
  • 来源身份索引:除精确键外还维护requestEntries(sourceKey → key)映射,readRequest()允许以“项目 + 请求 + 当前成员身份/角色”为来源视角查缓存,并在读取时丢弃同请求但来源不同的陈旧条目(discardStaleRequestEntries),这与侧边栏文档中“TeamReviewLoader 缓存身份包含 Project/request OIDs、请求元数据与当前成员身份和角色;评论与 Manager 转移可在 ref 未前进时使 review 失效”(sidebar/AGENTS.md)的规则一一对应。

缓存还有一组面向 publication review 的独立接口(storePublication/readPublication/discardPublication),以projectId + operationId + 各精确 OID为键(CollabPreparedReviewCache.ts),服务于文档中“stale-base 与 conflict-resolved 候选进入独立精确 publication review”的流转(见下文第 5 节)。

5. 跨表面不变量(Cross-Surface Invariants)

文档列出的跨表面不变量是整个协作交互模型的正确性边界,逐条解析如下:

5.1 冲突入口的唯一所有权

当前成员没有 open request 之前,个人冲突(personal conflict)从 “My changes”(我的工作区变更)发起;一旦存在 open request,该 request 就是唯一的冲突入口——包括其 base 前进后才检测到的冲突,此时由详情表面识别冲突归属位置。冲突呈现严格只读:成员或 Agent 通过编辑真实 Project 文件并再次 Publish来解决;这次 Publish 准备一次正常发布审查并更新同一个 request。已解决的 publication review 仍附着于同一 request,且不得重新出现为 “My changes” 的发布动作。

详情表面文档进一步落实了这一点:conflict/CollabConflictResolutionPanel.ts把 provider 无关的冲突会话呈现为不可变证据,不暴露任何“侧边选择、草稿编辑器、定稿操作、Git index stage/ref/marker 或 Agent 调用”(detail/AGENTS.md)。冲突读取始终绑定原始的 base / personal / accepted OID,即使工作文件已经变化;下一次 Publish 捕获精确的本地结果,用私有 scratch 暂存准备一次正常 publication review,并保持既有 Request 身份。

5.2 stale-base 与 publication review 的隔离

Stale-base 候选与冲突解决后的候选,在确认前会转移到独立的精确 publication review;publication-review 的文件永不进入 My changes 投影。反过来,只有 “My changes” 的工作树审查(working-tree review)可以打开可编辑的 Project 文件;request、publication 与 conflict 审查只展示精确审查内容,不得暴露该动作。从工作树审查发起 Publish 时,会为侧边栏保留任何已准备好的精确 publication review 并关闭工作树叶节点,向保留 review 的导航是显式的。这些约束在CollabFeaturePort的类型层面也有呼应:CollabPersonalChangesInspection区分publishreview-and-publishresolve-changes等个人动作(CollabFeaturePort.ts),CollabPublicationState区分committed-locallypushedrequest-synchronizedreview-required四个状态(CollabFeaturePort.ts)。

5.3 Ticket 表面的生命周期切分

Ticket 表面按生命周期切分:侧边栏负责过滤、分页与导航;详情负责创建/读取/编辑、评论、已接受的关系(accepted relations)以及关闭/重开。所有基于权限的变更保持 online-only。侧边栏的tickets/TicketListPanel.ts因此被约束为只包含 Open/Closed 过滤、Add 动作、分页行与详情导航,不得承载表单、正文、评论或状态变更(sidebar/AGENTS.md);缓存或过期(stale)的 Ticket 行必须标注只读并禁用 Add。

5.4 项目管理入口唯一性

项目管理只能从侧边栏项目头部动作打开。成员管理、邀请、Leave、Retire 与 LAN Host 控制保留在项目管理模态框中,不得在侧边栏重复出现。modals/project/目录下的ProjectManagementModal.tsProjectInvitationModal.tsLanHostSection.ts等文件即该入口的实现载体(见 modals/project 目录)。

5.5 响应式路由不触碰应用状态

navigation/ResponsiveCollabRouter.ts负责选择并显示一个兼容的 Claudian 表面,失败时回退到准备好的主标签视图,且不得变更聊天或 Collab 应用状态。其实现非常克制(ResponsiveCollabRouter.ts):先遍历已存在的候选目标做select+reveal,全部不兼容时回退创建主标签目标(失败被捕获为 null),任何目标抛错都返回 false 由上层处理——路由层自身不做状态副作用。

5.6 用户可见文案与可修复性

两条面向运维稳健性的不变量:

  • 文案分层:用户可见文案只描述 Projects、changes、Publish、review 与 recovery;Git refs、staging、branches、receive-pack 与数据库阶段仅作为高级诊断出现。这与CollabResultrecovery-required携带durablePhase(CollabFeaturePort.ts)的分工一致——应用状态可保留完整阶段信息,但呈现层默认不向用户倾倒 Git 术语。
  • 缺席不等于删除许可:工作副本缺失或设置被中断时,Project 必须保持可见且可修复;呈现代码永远不得把“本地记录缺失”解释为删除本地记录或 Host 权限的许可。侧边栏文档补充了 Retired Project 的处理:它仍在侧边栏可见,其摘要、重试、Keep 与 Delete 动作只使用本地生命周期投影,清理失败保持 Retired 并允许重试(sidebar/AGENTS.md)。

6. 验证要求:用测试固化上述边界

文档的 Verification 一节给出了两类必须覆盖的测试断言,它们本质上是把架构不变量“编译”成可执行的回归防线:

  • 跨表面测试必须覆盖:精确 prepared-review 的转移、个人冲突到 request 的冲突所有权移交、publication-review 的保留(retention)、以及 Ticket 导航——且不得把持久操作意图移入呈现状态;
  • 组合(composition)测试必须证明:插件onload不 awaitCollab 工作;布局就绪后的 Host 恢复保持后台执行;未保存 auto-start 意图的 Project 必须让 Git、SQL 与网络基础保持未触碰。

这些约束对应仓库中的测试面:tests/unit/features/collab/下的 27 个单元测试文件、tests/integration/app/collab/下的 50 个集成测试文件,以及测试辅助设施 CollabFeatureTestHarness.ts(用 fake port 驱动呈现表面)。子表面文档的 Verification 小节还列出了更细的断言清单,如“prepared-handoff 再验证、草稿/幂等保留、Accept 前置权限检查(preflight)、Pierre 复用/清理、有界连续渲染”(detail/AGENTS.md)与“隐藏失效合并、Project 切换、过期完成抑制、每面板取消”(sidebar/AGENTS.md)。

7. 小结:这套架构给多表面 UI 的启示

回到 src/features/collab/AGENTS.md 这条核心骨架,Claudian 的 Collab 呈现层用四条纪律支撑了复杂的多表面协作交互:

  1. 依赖方向是单向 DAG,shared/handoff 不反向依赖呈现表面,保证共享层可被任意表面安全复用;
  2. 可丢弃读取与持久操作严格分治——latest-task scope 只管可重放的呈现读取,持久操作交给应用层 admission + 幂等意图(MutationIntentStore);
  3. 表面切换只传有界元数据——CollabPreparedReviewCache以 8 条上限、5 分钟 TTL、精确 OID 键控,永不缓存 blob 与凭据;
  4. 不变量即测试——冲突入口唯一性、publication review 隔离、入口唯一性、缺席可修复等交互规则全部落到可回归的断言上。

对阅读源码的开发者而言,建议的深入路径是:先读 AGENTS.md 建立边界认知,再沿 CollabFeaturePort.ts 看契约如何结构化,然后分别进入 sidebar、detail、handoff 三个目录对照各表面的实现与子文档约束,最后在tests/unit/features/collab/中验证这些约束如何被测试固化。

【免费下载链接】claudianAn Obsidian plugin that embeds Claude Code/Codex as an AI collaborator in your vault项目地址: https://gitcode.com/GitHub_Trending/cl/claudian

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询