news 2026/9/6 18:31:48

Multica 的 AI 代理开发契约:AGENTS.md 仓库指南详解——架构分层、状态管理硬规则与数据库迁移约束

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Multica 的 AI 代理开发契约:AGENTS.md 仓库指南详解——架构分层、状态管理硬规则与数据库迁移约束

Multica 的 AI 代理开发契约:AGENTS.md 仓库指南详解——架构分层、状态管理硬规则与数据库迁移约束

【免费下载链接】multicaMake humans and AI agents work as one team — open-source and self-hostable.项目地址: https://gitcode.com/GitHub_Trending/mu/multica

AGENTS.md 是 Multica 仓库为 AI 代理(以及任何新加入的工程师)编写的「单一入口」开发指南:它声明了 Go 后端 + pnpm/Turborepo 前端 monorepo 的整体架构,并给出状态管理、包边界、数据库迁移四类不可妥协的硬规则。读完后你将掌握 Multica 各目录的职责划分、React Query 与 Zustand 的状态分工依据、跨 web/desktop 共享代码的边界约束,以及迁移系统刻意放弃整体事务的设计原因。

AGENTS.md 的定位:指针文档,而非规则本体

AGENTS.md 开头就声明了自己的角色:

Single source of truth:This file is a concise pointer document. All authoritative architecture, coding rules, and conventions live inCLAUDE.mdat the project root.

也就是说,这份文件是「快速参考 + 指针」:完整的权威规则在同级目录的 CLAUDE.md 中(例如命令列表以 Makefile、package.json、pnpm-workspace.yaml 为准)。对 AI 代理来说,这种分层设计是刻意为之——代理先读 AGENTS.md 建立仓库心智模型,需要更深规则(乐观更新四条件、API 兼容性、UUID 处理、测试分层表等)时再跳转 CLAUDE.md,避免两份文档互相复制后失同步。

架构总览:Go 后端 + 共享包分层的前端 monorepo

AGENTS.md 的 Quick Reference 给出了一张目录级架构图:

Go backend + monorepo frontend (pnpm workspaces + Turborepo) with shared packages. - server/ - Go backend (Chi router, sqlc, gorilla/websocket) - apps/web/ - Next.js frontend (App Router) - apps/desktop/ - Electron desktop app - apps/mobile/ - Expo / React Native iOS app (read apps/mobile/CLAUDE.md first) - apps/docs/ - Fumadocs documentation site - packages/core/ - Headless business logic (Zustand stores, React Query hooks, API client) - packages/ui/ - Atomic UI components (shadcn/Base UI, zero business logic) - packages/views/ - Shared business pages/components - packages/tsconfig/ - Shared TypeScript config - packages/eslint-config/ - Shared ESLint config

这条描述可以与仓库实际内容一一印证:

  • 后端:server/go.mod 中声明了github.com/go-chi/chi/v5 v5.3.0github.com/gorilla/websocket v1.5.3,与 AGENTS.md 的「Chi router、gorilla/websocket」一致;sqlc 代码生成由make sqlc驱动(Makefile 中的sqlc:目标注释为 "Regenerate sqlc code")。
  • 工作区:pnpm-workspace.yaml 只声明了apps/*packages/*两组 glob,与目录树一一对应;根 package.json 的dev:web/build/typecheck等脚本全部通过turbo ... --filter=调度,engines要求node >= 22,与 CLAUDE.md 中「CI runs Node 22」的表述吻合。
  • 移动端是孤岛:文档特意注明进apps/mobile/之前先读 apps/mobile/CLAUDE.md。根 package.json 里build/typecheck/test/lint全部带--filter=!@multica/mobile,从源码结构看,移动端确实被显式排除在统一的 Turborepo 流水线之外,拥有独立的 React 版本与构建管线。

CLAUDE.md 进一步补充了一条依赖方向规则:共享包以原始.ts/.tsx源码导出、由消费方应用编译,依赖方向是views -> core + ui,且coreui必须保持相互独立。

状态管理(critical):React Query 管服务端,Zustand 管客户端

这是 AGENTS.md 中标注 critical 的章节,四条规则逐条展开:

  1. React Query 拥有全部服务端状态——issues、members、agents、inbox、workspace 列表等一切来自 API 的数据;
  2. Zustand 拥有客户端/视图状态——视图过滤器、草稿、模态框、桌面端 tab 状态;当前 workspace 身份由路由驱动,仅向平台层镜像(用于请求头、存储命名空间、WebSocket 重连);
  3. 所有 Zustand store 必须放在packages/core/,禁止出现在packages/views/或各 app 目录;
  4. WS 事件更新 React Query 缓存;store 只允许用于「清空客户端自己持有的指针」,且必须带单一响应者/自事件守卫。

仓库中的实际代码印证了第 3 条:Zustand 的create()调用集中在packages/core/下的各域,例如 view-store.ts、actor-issues-view-store.ts、my-issues-view-store.ts 与 config store。而 CLAUDE.md 对第 4 条给出了更严的操作定义:WebSocket 事件只能 invalidate 或 patch Query 缓存,绝不许把服务端 payload 镜像进 Zustand;只有当「本客户端自己可能触发了该事件」时,才允许清理 active session、selection 等客户端指针,且必须通过 self-initiated guard 防止自己发的消息又把自己清理掉。

这条规则的工程动机从仓库结构也能看出:web 与 desktop 共享同一套packages/core/的 hooks 和 stores,如果 WS 事件写 Zustand 缓存数据,两端各自实现一次守卫,很容易在 Electron 多窗口(每个渲染进程一个 WS 连接)场景下产生竞争——而「单一响应者 + 自事件守卫」正是为多窗口环境设计的。

版本层面,pnpm-workspace.yaml 的catalog:段锁定了@tanstack/react-query: ^5.96.2zustand: ^5.0.0,即文档中的 React Query 指 TanStack Query v5。

包边界(hard rules):用依赖方向换取三端共享

AGENTS.md 的四条硬边界:

包/目录硬约束
packages/core/react-dom、零localStorage、零process.env
packages/ui/@multica/core导入
packages/views/next/*、零react-router-dom,路由一律走NavigationAdapter
apps/web/platform/Next.js API 的唯一落点

这组约束的本质是:coreui互不依赖,views依赖两者,于是同一份业务代码能同时被 Next.js(web)和 Electron(desktop)两个平台编译。CLAUDE.md 补充了对应的正向做法与额外约束:

  • core中持久化要用StorageAdapter而非localStorage,让桌面端可以换成自己的存储;
  • packages/views/使用NavigationAdapteruseNavigation()<AppLink>做路由抽象;apps/desktop/src/renderer/src/platform/react-router-dom的唯一接线处;
  • 每个 workspace 必须在自己package.json中声明直接导入的外部依赖,共享依赖版本统一走pnpm-workspace.yamlcatalog:机制(apps/mobile/例外,直接钉住 Expo/React Native 相关版本)。

「零react-dom、零localStorage、零process.env」这条规则之所以值得单独强调,是因为这三样恰好是 headless 包在 SSR(Next.js 服务端渲染)和 Electron 主进程环境下最容易踩的雷:SSR 阶段没有window.localStorage,Electron 中process.env的注入方式与浏览器完全不同。把约束钉死在包边界上,而不是依赖开发者自觉,是该仓库共享代码规模能做大的前提。

数据库迁移(hard rules):禁外键 + 索引必须 CONCURRENTLY

AGENTS.md 给出两条迁移硬规则,它们都能在源码中找到落点:

1. 禁止外键与级联

Never add database foreign keys or cascading actions. Enforce relationships and perform dependent cleanup explicitly in the application layer, using transactions when the operation must be atomic.

即:关系校验与依赖清理全部显式写进应用代码;当清理必须与父操作原子提交/回滚时,用应用层事务。CLAUDE.md 的表述一致:禁止FOREIGN KEY/REFERENCES、级联删除、级联更新。

2. 每个索引必须CREATE [UNIQUE] INDEX CONCURRENTLY,且单独成文件

Every index created by a migration, including unique indexes and indexes on new tables, must useCREATE [UNIQUE] INDEX CONCURRENTLY. Keep each concurrent index build in its own single-statement migration file.

仓库的迁移目录大量遵循该模式,例如 170_skill_label_lookup_index.up.sql、418_seat_capacity_due_index.up.sql 等均使用CREATE INDEX CONCURRENTLY

为什么必须单独成文件?答案在迁移执行器源码里。server/cmd/migrate/main.go 的注释写得很直白:

// We deliberately do NOT wrap the loop in a single transaction: the // repo already ships migrations using CREATE INDEX CONCURRENTLY, // which Postgres rejects inside a transaction block.

迁移循环刻意包在单一事务里(同时用pg_advisory_lock固定一条pgxpool.Conn做会话级锁,避免锁挂在被回收的随机连接上)。因为 PostgreSQL 拒绝在事务块内执行并发建索引,所以每个 CONCURRENTLY 语句必须独占一个单语句迁移文件。

CLAUDE.md 还补充了一条容易被忽略的规则:条件跳过的迁移仍会记入schema_migrations,因此台账只证明顺序、不证明每条 SQL 都执行过;后续涉及「条件存在的对象」的迁移必须写幂等 DDL(IF EXISTS/IF NOT EXISTS)。server/cmd/migrate/README.md 就给出了一个真实运维案例:迁移 371 在pg_bigm可用时建 bigram 索引、否则回退pg_trgm索引,一旦回退索引被误删,需要手工在事务外逐条执行CREATE INDEX CONCURRENTLY恢复,并用pg_indexindisvalid/indisready/indislive三个标志验证后才可恢复流量。

命令速查:从文档到可运行的验证管线

AGENTS.md 给出的最小命令集:

make dev # Auto-setup + start everything pnpm typecheck # TypeScript check pnpm test # TS unit tests (Vitest) make test # Go tests make check # Full verification pipeline

对照仓库实现,这些命令的真实行为是:

  • Makefile 的dev:目标注释为 "Bootstrap this checkout end-to-end: create env if needed, ensure DB, migrate, start services"——即自动建环境、确保数据库、跑迁移、起服务;test:目标会在跑 Go 测试前先确保目标库存在且迁移已应用;
  • check:目标执行 "Run typecheck, TS tests, Go tests, and Playwright E2E for the current checkout",实际委托给 scripts/check.sh,其内部管线为typecheck → 单测 → Go 测试 → E2E(check.sh 开头注释即 "Full verification pipeline: typecheck → unit tests → Go tests → E2E");
  • package.json 的test脚本是turbo test --filter=!@multica/mobile,即 Vitest 单测经 Turborepo 调度且排除移动端,与文档中「TS unit tests (Vitest)」对应。

CLAUDE.md 在此基础上给出完整的开发环境命令族(make up/status/list/down/destroy/worktree-env等)及其细节:make up把每个开发环境登记到~/.multica/dev/,在锁下分配 API/Web/Desktop 端口与数据库名,并通过DATABASE_URL而非docker exec验证数据库;worktree 之间共享一个 PostgreSQL 容器,用.env.worktree隔离库名与端口。此外 CI 环境为 Node 22 + 最新 Go 1.26 patch +pgvector/pgvector:pg17PostgreSQL 服务。

总结

AGENTS.md 作为指针文档的价值在于「少而硬」:它把 Multica 真正容易出错的四件事——目录职责、服务端/客户端状态归属、共享包依赖方向、迁移 DDL 约束——压缩成一张速查表,其余细节通过 CLAUDE.md 与 Makefile/pnpm-workspace.yaml 这三个「单一事实源」继续下钻。如果你在 AI 代理协助下向这个仓库提交代码,值得逐字对照的正是文中两个 (hard rules) 章节:包边界违反会让三端共享代码退化,迁移规则违反则可能让CONCURRENTLY建索引在事务里直接失败或阻塞线上写入。

【免费下载链接】multicaMake humans and AI agents work as one team — open-source and self-hostable.项目地址: https://gitcode.com/GitHub_Trending/mu/multica

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/6 18:28:57

ABS《钢质船舶建造与入级规范》核心逻辑与实操要点详解

简介&#xff1a;美国船级社&#xff08;ABS&#xff09;《钢质船舶建造与分类规则——内河及近海水道运营船舶适用》2023年7月版正式发布&#xff0c;面向船舶设计、建造、检验、航运及海事工程技术人员&#xff0c;为航行于河流和沿海水域的钢质船舶提供了从设计、建造到运营…

作者头像 李华