ZCode 的工程纪律以 spec-first + 单一状态所有者 + 类型/引用边界 为核心,通过两层 AGENTS.md + architecture skill + pre-push 三段门禁落地。
版本锚点与工具链
| 工具 | 版本 | 来源 |
|---|---|---|
| Node | >=24.0.0,精确 24.14.0 | 根 package.json、mise.toml |
| pnpm | 10.33.2 | package.json、mise.toml |
| TypeScript | 根 ^6.0.2,CLI 子库 ^5.9.0 | 各自 package.json |
mise.toml 是权威版本源(AGENTS.md 明确”Node 版本以 mise.toml 为准”),还定义环境变量与 6 个 task 包装器(经 scripts/mise-run.mjs 注入工具链环境)。.npmrc 使用 node-linker=hoisted(SEA 打包依赖)。
构建编排:分层(根 pnpm,CLI turbo)
- 根没有
turbo.json。根编排靠 pnpm:build=pnpm -r build;build:bootstrap= 过滤所有packages/*排除 desktop,再单跑@zcode/desktop build:no-runtime-assets。 - 唯一 Turbo 配置在
apps/zcode-cli/turbo.json:定义 build(dependsOn:["^build"],带 cache)、build:desktop-agent、test/coverage(dependsOn:["build"])、typecheck、dev、clean,以及@zcode/cli#buildoverride。 scripts/build-desktop-agent-cli.mjs默认不走 turbo,而按显式有序包清单逐个pnpm --filter <pkg> build(原因:Linux 容器 demo 无仓库级 turbo 根,turbo --cwd apps/zcode-cli会把子目录当根并拒绝指向../../packages/shared的 inputs)。- TS 用 project references 全链路:
tsconfig.base.json的composite:true、declaration+declarationMap+sourceMap、module/moduleResolution: nodenext、target/lib: es2024、verbatimModuleSyntax、noUncheckedIndexedAccess。根typecheck是一条显式列 15 个入口的复合tsc -b。
关键构建脚本(scripts/,80+)
| 脚本 | 作用 |
|---|---|
bootstrap.mjs | git submodule update --init(CLI 是 submodule)+ pnpm install + prepare:desktop-runtime(默认跳过远程资源)+ build:bootstrap |
build-zcode.mjs | 统一 Node 分发包:构建 CLI 闭包 → server → web → 组装 → tar.gz + sha256 + latest.json + install.sh |
build-desktop-agent-cli.mjs | 按有序清单构建 12 个 CLI 包 + 2 个必需插件 runtime + 暂存 bundled-agents/ |
dependency-graph.mjs / dep-refs.mjs | 生成 mermaid/dot/json 依赖图 / ts-morph 精确查 export 引用 |
count-lines.sh | 找 >400 行的 .ts |
clean.mjs | 安全删 node_modules/dist(含路径断言) |
assert-node-version.mjs | 强制 Node ≥ 24 |
native-search-tools-*.mjs(8 个) | rg/bfs/ugrep sidecar 构建与打包 |
其余:build-sea、build-native-search、prepare-prebuilds、download-glm、third-party-notices、zcode-distribution/{assets,installer,runner}、dev-desktop-env、run-affected-unit-tests、release-it/*、architecture/*。详见 distribution。
架构治理
策略模型(architecture-policy.yaml,195 行)
可执行的组织级契约,version: 1,三类要素:
- modules(35+ 条):每条含
id/roots/managed/requires/publicEntrypoints/layers/layerOrder/owner。存量模块多标managed:false;已迁移的managed:true模块:storage、gen-ui-protocol/gen-ui-service/gen-ui、mcp-apps-protocol/mcp-ui/plugin-ui-bridge/plugin-sandbox/plugin-ui。 - global 限额:
maxFileLines: 400、maxContractLines: 300、maxPublicMethods: 12、forbidCycles: true、forbidDeepImports: true、managedOnly: true。 - exceptions:仅两条,都针对
genUiTweakRuntime.js(放宽行数与 disable-count),各带reason。
检查器(scripts/architecture/)
architecture-check.mjs(CLI:check/report/baseline:update/context,支持 --changed)+ index.mjs(规则引擎)实现:
missing-module-artifact:managed 模块须有module.ts/contract.ts/contract.example.ts/contract.test.ts/CONTRACT.md五件套max-file-lines/max-contract-lines/max-public-methodsdisable-count:禁止任何oxlint-disable/eslint-disabledomain-io:domain 层不得 importnode:/fs|path|http|https|net|child_process|timers,不得出现fetch/setTimeout/setIntervallayer-direction:层序靠前的层不能依赖更靠后的层ui-implementation-import:ui模块不得直接依赖 repo/runtime/service 实现module-dependency:跨模块 import 必须在requires中声明deep-import:跨模块只能走publicEntrypointscycle:DFS 环检测(仅 managed 模块间)
Ratchet baseline
.architecture-baseline.json 当前为空({"version":1,"violations":[]})。违规用 sha256(rule\0file\0detail)[:16] 指纹;命中 baseline 的是”存量”(不阻断),新指纹才 FAIL。--changed 从变更文件沿反向依赖图求影响闭包。baseline:update 只在显式调用时写盘,CI 从不自动刷新。
architecture-governance skill
.agents/skills/architecture-governance/SKILL.md:编码前先跑 architecture:check --changed 拿基线;跨模块/契约不明时跑 architecture:context <module-id>;强制五条设计决策(One owner / One path / Explicit boundaries / Explicit time / Bounded context)。
边界:
managedOnly:true+ 空 baseline 意味着零违规只保证已完成迁移的这批模块,不证明全部存量跨域依赖合规。
测试矩阵
| 层级 | 入口 | 运行器/隔离 |
|---|---|---|
| 单测 | pnpm test:unit / test | Vitest threads(unit)+ forks(fork-isolated) |
| 覆盖率 | pnpm test:coverage | Vitest v8 |
| affected | pnpm test:unit:affected | lint-staged + vitest run / vitest related |
| CLI 单测 | pnpm --dir apps/zcode-cli test | Turbo + node:test + vitest |
| 桌面 e2e | pnpm test:e2e | WDIO Electron,Windows 分片 |
| 容器 e2e | pnpm test:e2e:container[:preset] | Docker,replay-isolated 默认 |
| native-search e2e | pnpm test:native-search:e2e | 独立 Bash toolcall |
| SEA runtime e2e | pnpm test:sea-runtime-tools:e2e | 单文件交付验证 |
| 架构测试 | pnpm test:architecture | node --test scripts/test/architecture-governance.test.mjs |
规模(实测):根单测 1917 个、desktop WDIO e2e 417 个、managed contract test 7 个。单测与 e2e 通过 --exclude "packages/desktop/test/e2e/**/*.test.ts" 硬分层。
vitest.config.ts 定义两个 project:unit(threads,maxWorkers 4)与 fork-isolated(串行 forks)。forkIsolatedTests 手工白名单每个条目都对应一个真实故障(进程树/生命周期、electron-builder NSIS 下载、真 Git 子进程、pdfjs 原生崩溃等),带中文根因注释,是重点维护区。
vitest.setup.ts:Windows Git Bash PATH 修正、强制 core.autocrlf=false、固定 release target、删除 ZCODE_DATA_BASE_DIR 防误写真实目录、mock homedir。
Lint / Format / Hooks / Release
- oxlint(
.oxlintrc.json):核心规则只有一条max-lines: ["error", {max:400, skipBlankLines:true, skipComments:true}];测试文件与 i18n locale 关闭;忽略apps/zcode-cli、vendored UI、.agents/skills等。不启用推荐规则集。 - oxfmt(
.oxfmtrc.json):fmt/fmt:check。 - husky + lint-staged:
.husky/pre-push缓存 stdin(供 affected 取 remote sha)、unset $(git rev-parse --local-env-vars),然后pnpm run verify:pre-push。 verify:pre-push=pnpm run lint && pnpm run architecture:check -- --changed && pnpm run test:unit:affected。- release-it:根
.release-it.mjs提交chore: release v${version}+ tag + push,但关闭 github/gitlab/npm release;CLI 有独立 release 配置。
CI(GitLab,配置未随仓库导出)
重要事实:仓库没有 .github/,也没有 .gitlab-ci.yml。CI 是 GitLab,作业定义属闭源/未导出部分;scripts/ci/(如 ci-lint-pipeline.mjs)同样不在检出中。以下从文档还原:
- 阶段(
docs/desktop-ci-cd.md):test → build-app → build → notify → approve → validate → upload → preload → publish → release-notify。 - 构建平台选择(
docs/ci-build-platform-selection.md):变量ZCODE_BUILD_PLATFORMS(六平台);MR 默认macos-arm64,windows-x64;tag 与ci/*六平台全跑。 - 缓存策略(
docs/ci-cache-strategy.md):废弃 GitLab cache,改用 runner 工作区持久化(.pnpm-store/.npm-cache/.next/cache/mock-cdn靠GIT_CLEAN_FLAGS排除项复用)。build:desktop:app11m10s → ~25s,MR 流水线 ~34min → ~23min。 - 飞书通知(
docs/ci-feishu-card2-notification.md):notify:feishu:<platform>:<arch>发 Feishu Card 2.0,经 zcode-internal 通用服务转发(CI 不持有飞书应用凭证)。
工程纪律(AGENTS.md)
根 AGENTS.md(82 行)核心:spec-first、以当前检出的源码/package.json/架构策略为准、先诊断后修改、保留无关本地改动、开工前跑 check-workspace-freshness.mjs、必跑 pnpm typecheck 和 pnpm lint 并报真实结果、避免重复状态和多条写入路径(不能用超时掩盖同步问题)、跨包导入用公开入口、平台操作走 IPlatformService、日志分层。
apps/zcode-cli/AGENTS.md(103 行):工具改动必须先读/更新 docs/design/v2/tool/00-tool-change-chain.md;单文件默认 ≤400 行;常量提取为命名变量;环境变量统一 ZCODE_ 前缀;外部 I/O 边界收敛(副作用必须可观察/审批/取消/重试/排队/审计/测试;业务模块不得直接调 fetch/http/fs/child_process/process.env);traceId 贯穿;错误是一等设计对象,底层不调 process.exit;每个功能级变更一个独立提交。
patches/
6 个补丁通过 pnpm.patchedDependencies 应用:vitest@4.1.7、wdio-electron-service@9.2.1、@arms/rum-electron@0.0.3、@ai-sdk/openai-compatible@2.0.60、@ai-sdk/anthropic@3.0.81、@open-pencil/core@0.14.0(磁盘存在但未登记)。pnpm.overrides 强制 react/react-dom = 19.2.7。
相关页面
- monorepo — 包结构、依赖图
- architecture — 模块边界对应运行层
- distribution — 打包与分发流水线
- glossary — 术语表