测试与工程化
eec0843ce422
Agent 生成档案
Section titled “Agent 生成档案”- 章节 ID:
13-testing-engineering - 章节摘要:沿一次 package 级改动的验证路径,理解 workspace、Turbo 任务图、测试根目录保护、类型检查和 SDK 生成如何形成工程信任链。
- 教程版本:
eec0843c - 源码基线:
eec0843ce42298080569ca31a6455bc3f699d213 - 章节元数据:/versions/eec0843c/data/chapters.json
- 源码映射:/versions/eec0843c/data/source-map.json
主要源码路径
Section titled “主要源码路径”package.jsonturbo.jsontsconfig.jsonAGENTS.mdpackages/opencode/package.jsonpackages/sdk/js/package.json
源码基线:
eec0843ce422。本章描述的是仓库声明的任务边界;未实际运行的 OpenCode 测试不会写成“已经通过”。
0. 本章学习目标
Section titled “0. 本章学习目标”读完本章,你应该能:
- 画出 workspace、catalog、package scripts 与 Turbo task graph 的关系。
- 解释为什么 OpenCode 故意禁止从仓库根目录运行测试。
- 为一次核心 runtime 改动选择正确的 typecheck、test 与 build 边界。
- 区分“任务存在”“任务被编排”“本次已经执行并通过”。
- 为 mini agent 设计一条按风险递增的验证阶梯。
1. 一句话讲明白
Section titled “1. 一句话讲明白”OpenCode 的工程化不是一个万能的根目录命令,而是“根目录管理包与任务图,子 package 对自己的类型、测试和构建负责”,让验证范围与真实模块边界对齐。
中心问题是:在一个包含 runtime、SDK 和多个 UI 的 monorepo 里,怎样既避免漏测,又避免每改一行都盲跑全世界?
2. 先画地图:谁定义什么
Section titled “2. 先画地图:谁定义什么”root package.json +-- workspaces:有哪些 package +-- catalog:共享依赖版本 +-- root scripts:跨包入口与保护栏 | v turbo.json 任务依赖与产物 | +------+----------------+ v vpackages/opencode packages/sdk/jstypecheck/test/build typecheck/build根目录决定“有哪些队伍、公共版本是什么、跨队依赖怎样排”;package 决定“本队如何验证”。
Java 开发者可以类比 Gradle multi-project + version catalog + task graph。但 Bun workspace 脚本不是 Maven 生命周期,不能凭 test 这个名字猜执行范围。
3. 最小机制:按影响面选择任务
Section titled “3. 最小机制:按影响面选择任务”一个可迁移的最小工程化策略是:
change(files): owners = packagesOwning(files) run fast static checks for owners run focused tests near changed behavior run dependent builds/tests from task graph only then run broader regression required for deliveryOpenCode 把部分依赖写进 turbo.json:例如 opencode#test 依赖上游 package build,见 turbo.json:11-15。
turbo.json
turbo.json:11-15
11 "opencode#test": {12 "dependsOn": ["^build"],13 "outputs": [],14 "passThroughEnv": ["*"]15 },
这不是“永远只跑最小测试”。它是先用依赖图找到必要范围,再根据发布风险扩展回归。
4. 三种容易混淆的事实
Section titled “4. 三种容易混淆的事实”| 看到什么 | 能证明什么 | 不能证明什么 |
|---|---|---|
package 有 test script | 项目定义了测试入口 | 当前测试已经通过 |
Turbo 有 dependsOn | 经 Turbo 调度时存在依赖 | 手工进入 package 执行会自动跑上游 |
dist/** 声明为 outputs | Turbo 可把它当构建产物 | 产物内容正确、可发布 |
工程文档最常见的失真,是把“配置上存在”写成“本次验证成功”。本章只做源码声明解读。
5. 最小源码路径
Section titled “5. 最小源码路径”package.json:7-21:runtime 版本、根脚本和禁止 root test。
package.json
package.json:7-21
7 "packageManager": "bun@1.3.14",8 "scripts": {项目脚本入口。9 "dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",常用工程命令。10 "dev:desktop": "bun --cwd packages/desktop dev",11 "dev:web": "bun --cwd packages/app dev",12 "dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev",13 "dev:storybook": "bun --cwd packages/storybook storybook",14 "lint": "oxlint",常用工程命令。15 "typecheck": "bun turbo typecheck",常用工程命令。16 "upgrade-opentui": "bun run script/upgrade-opentui.ts",17 "postinstall": "bun run --cwd packages/opencode fix-node-pty",18 "prepare": "husky",19 "random": "echo 'Random script'",20 "hello": "echo 'Hello World!'",21 "test": "echo 'do not run tests from root' && exit 1"常用工程命令。
package.json:23-87:workspace 与依赖 catalog。
package.json
package.json:23-87
23 "workspaces": {声明工作区范围。24 "packages": [25 "packages/*",26 "packages/console/*",27 "packages/sdk/js",28 "packages/slack"29 ],30 "catalog": {31 "@effect/opentelemetry": "4.0.0-beta.65",32 "@effect/platform-node": "4.0.0-beta.65",33 "@npmcli/arborist": "9.4.0",34 "@types/bun": "1.3.13",35 "@types/cross-spawn": "6.0.6",36 "@octokit/rest": "22.0.0",37 "@hono/zod-validator": "0.4.2",38 "@opentui/core": "0.2.14",39 "@opentui/keymap": "0.2.14",40 "@opentui/solid": "0.2.14",41 "ulid": "3.0.1",42 "@kobalte/core": "0.13.11",43 "@types/luxon": "3.7.1",44 "@types/node": "24.12.2",45 "@types/semver": "7.7.1",46 "@tsconfig/node22": "22.0.2",47 "@tsconfig/bun": "1.0.9",48 "@cloudflare/workers-types": "4.20251008.0",49 "@openauthjs/openauth": "0.0.0-20250322224806",50 "@pierre/diffs": "1.1.0-beta.18",51 "opentui-spinner": "0.0.6",52 "@solid-primitives/storage": "4.3.3",53 "@tailwindcss/vite": "4.1.11",54 "diff": "8.0.2",55 "dompurify": "3.3.1",56 "drizzle-kit": "1.0.0-beta.19-d95b7a4",57 "drizzle-orm": "1.0.0-beta.19-d95b7a4",58 "effect": "4.0.0-beta.65",59 "ai": "6.0.168",60 "cross-spawn": "7.0.6",61 "hono": "4.10.7",62 "hono-openapi": "1.1.2",63 "fuzzysort": "3.1.0",64 "luxon": "3.6.1",65 "marked": "17.0.1",66 "marked-shiki": "1.2.1",67 "remend": "1.3.0",68 "@playwright/test": "1.59.1",69 "semver": "7.7.4",70 "typescript": "5.8.2",71 "@typescript/native-preview": "7.0.0-dev.20251207.1",72 "zod": "4.1.8",73 "remeda": "2.26.0",74 "shiki": "3.20.0",75 "solid-list": "0.3.0",76 "tailwindcss": "4.1.11",77 "virtua": "0.49.1",78 "vite": "7.1.4",79 "@solidjs/meta": "0.29.4",80 "@solidjs/router": "0.15.4",81 "@solidjs/start": "https://pkg.pr.new/@solidjs/start@dfb2020",82 "@sentry/solid": "10.36.0",83 "@sentry/vite-plugin": "4.6.0",84 "solid-js": "1.9.10",85 "vite-plugin-solid": "2.11.10",86 "@lydell/node-pty": "1.2.0-beta.10"87 }
turbo.json:5-42:task graph、依赖和 outputs。
turbo.json
turbo.json:5-42
5 "tasks": {6 "typecheck": {},常用工程命令。7 "build": {常用工程命令。8 "dependsOn": [],9 "outputs": ["dist/**"]10 },11 "opencode#test": {12 "dependsOn": ["^build"],13 "outputs": [],14 "passThroughEnv": ["*"]15 },16 "test:ci": {17 "outputs": [".artifacts/unit/junit.xml"],18 "passThroughEnv": ["*"]19 },20 "opencode#test:ci": {21 "dependsOn": ["^build"],22 "outputs": [".artifacts/unit/junit.xml"],23 "passThroughEnv": ["*"]24 },25 "@opencode-ai/app#test": {26 "dependsOn": ["^build"],27 "outputs": []28 },29 "@opencode-ai/app#test:ci": {30 "dependsOn": ["^build"],31 "outputs": [".artifacts/unit/junit.xml"],32 "passThroughEnv": ["*"]33 },34 "@opencode-ai/ui#test": {35 "dependsOn": ["^build"],36 "outputs": []37 },38 "@opencode-ai/ui#test:ci": {39 "dependsOn": ["^build"],40 "outputs": [".artifacts/unit/junit.xml"],41 "passThroughEnv": ["*"]42 }
tsconfig.json:1-5:根 TypeScript 基线有意保持很薄。AGENTS.md:119-127:测试与 typecheck 的人工契约。
AGENTS.md
AGENTS.md:119-127
119## Testing文档标题层级。120121- Avoid mocks as much as possible列表里的一个要点。122- Test actual implementation, do not duplicate logic into tests列表里的一个要点。123- Tests cannot run from repo root (guard: `do-not-run-tests-from-root`); run from package dirs like `packages/opencode`.列表里的一个要点。124125## Type Checking文档标题层级。126127- Always run `bun typecheck` from package directories (e.g., `packages/opencode`), never `tsc` directly.列表里的一个要点。
packages/opencode/package.json:8-19:核心 runtime 的验证入口。
packages/opencode/package.json
packages/opencode/package.json:8-19
8 "scripts": {项目脚本入口。9 "typecheck": "tsgo --noEmit",常用工程命令。10 "test": "bun test --timeout 30000",常用工程命令。11 "test:ci": "mkdir -p .artifacts/unit && bun test --timeout 30000 --reporter=junit --reporter-outfile=.artifacts/unit/junit.xml",12 "test:httpapi": "bun run script/httpapi-exercise.ts --mode coverage --fail-on-missing --fail-on-skip && bun run script/httpapi-exercise.ts --mode auth --fail-on-missing --fail-on-skip && bun run script/httpapi-exercise.ts --mode effect --fail-on-missing --fail-on-skip",13 "bench:test": "bun run script/bench-test-suite.ts",14 "profile:test": "bun run script/profile-test-files.ts",15 "build": "bun run script/build.ts",常用工程命令。16 "fix-node-pty": "bun run script/fix-node-pty.ts",17 "dev": "bun run --conditions=browser ./src/index.ts",常用工程命令。18 "dev:temporary": "bun run --conditions=browser ./src/temporary.ts",19 "db": "bun drizzle-kit"
packages/opencode/package.json:21-37:bin 与 Bun/Node 条件导入。
packages/opencode/package.json
packages/opencode/package.json:21-37
21 "bin": {22 "opencode": "./bin/opencode"23 },24 "exports": {包对外暴露入口。25 "./*": "./src/*.ts"26 },27 "imports": {28 "#db": {29 "bun": "./src/storage/db.bun.ts",30 "node": "./src/storage/db.node.ts",31 "default": "./src/storage/db.bun.ts"32 },33 "#pty": {34 "bun": "./src/pty/pty.bun.ts",35 "node": "./src/pty/pty.node.ts",36 "default": "./src/pty/pty.bun.ts"37 }
packages/sdk/js/package.json:7-18:SDK 的 typecheck、生成构建和 exports。
packages/sdk/js/package.json
packages/sdk/js/package.json:7-18
7 "scripts": {项目脚本入口。8 "typecheck": "tsgo --noEmit",常用工程命令。9 "build": "bun ./script/build.ts"常用工程命令。10 },11 "exports": {包对外暴露入口。12 ".": "./src/index.ts",13 "./client": "./src/client.ts",14 "./server": "./src/server.ts",15 "./v2": "./src/v2/index.ts",16 "./v2/client": "./src/v2/client.ts",17 "./v2/gen/client": "./src/v2/gen/client/index.ts",18 "./v2/server": "./src/v2/server.ts"
6. 一条具体工程旅程:修改 session API 后该怎么验证
Section titled “6. 一条具体工程旅程:修改 session API 后该怎么验证”假设你修改了核心 runtime 的 session HTTP API,并因此需要重新生成 JS SDK。下面是从仓库契约推导出的验证路径,不表示这些命令在本章编写时已经执行。
第一步:先定位 package ownership
Section titled “第一步:先定位 package ownership”服务端改动属于 packages/opencode;generated client 属于 packages/sdk/js。根 workspaces.packages 明确包含 packages/* 与 packages/sdk/js,见 package.json:23-29。
package.json
package.json:23-29
23 "workspaces": {声明工作区范围。24 "packages": [25 "packages/*",26 "packages/console/*",27 "packages/sdk/js",28 "packages/slack"29 ],
所以这是跨两个 package 的改动,不能只对根 tsconfig.json 跑一次编译就结束。
第二步:用 package 声明的方式做 typecheck
Section titled “第二步:用 package 声明的方式做 typecheck”核心包声明:
typecheck = tsgo --noEmit见 packages/opencode/package.json:8-10。SDK 也有自己的 tsgo --noEmit,见 packages/sdk/js/package.json:7-10。
packages/opencode/package.json
packages/opencode/package.json:8-10
8 "scripts": {项目脚本入口。9 "typecheck": "tsgo --noEmit",常用工程命令。10 "test": "bun test --timeout 30000",常用工程命令。
packages/sdk/js/package.json
packages/sdk/js/package.json:7-10
7 "scripts": {项目脚本入口。8 "typecheck": "tsgo --noEmit",常用工程命令。9 "build": "bun ./script/build.ts"常用工程命令。10 },
仓库规范进一步要求从 package 目录运行 bun typecheck,不要直接调用 tsc,见 AGENTS.md:125-127。原因不是语法偏好,而是 package script 才包含仓库选择的编译器和参数。
AGENTS.md
AGENTS.md:125-127
125## Type Checking文档标题层级。126127- Always run `bun typecheck` from package directories (e.g., `packages/opencode`), never `tsc` directly.列表里的一个要点。
第三步:重新生成 SDK 是显式步骤
Section titled “第三步:重新生成 SDK 是显式步骤”SDK package 的 build 指向 bun ./script/build.ts,见 packages/sdk/js/package.json:7-10;根 AGENTS.md:1 也明确指出 SDK 生成入口。
packages/sdk/js/package.json
packages/sdk/js/package.json:7-10
7 "scripts": {项目脚本入口。8 "typecheck": "tsgo --noEmit",常用工程命令。9 "build": "bun ./script/build.ts"常用工程命令。10 },
AGENTS.md
AGENTS.md:1
1- To regenerate the JavaScript SDK, run `./packages/sdk/js/script/build.ts`.列表里的一个要点。
因此,API contract 改了但没有更新 generated client,是一个可预见的交付缺口。生成物是否变化、类型是否对应,都应该在 diff 与 SDK typecheck 中验证。
第四步:核心测试必须在 package 内运行
Section titled “第四步:核心测试必须在 package 内运行”根 package.json 的 test 会打印禁止信息并退出 1,见 package.json:8-21。AGENTS.md 同时要求尽量少 mock、测试真实实现,并从具体 package 运行测试,见 AGENTS.md:119-123。
package.json
package.json:8-21
8 "scripts": {项目脚本入口。9 "dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",常用工程命令。10 "dev:desktop": "bun --cwd packages/desktop dev",11 "dev:web": "bun --cwd packages/app dev",12 "dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev",13 "dev:storybook": "bun --cwd packages/storybook storybook",14 "lint": "oxlint",常用工程命令。15 "typecheck": "bun turbo typecheck",常用工程命令。16 "upgrade-opentui": "bun run script/upgrade-opentui.ts",17 "postinstall": "bun run --cwd packages/opencode fix-node-pty",18 "prepare": "husky",19 "random": "echo 'Random script'",20 "hello": "echo 'Hello World!'",21 "test": "echo 'do not run tests from root' && exit 1"常用工程命令。
AGENTS.md
AGENTS.md:119-123
119## Testing文档标题层级。120121- Avoid mocks as much as possible列表里的一个要点。122- Test actual implementation, do not duplicate logic into tests列表里的一个要点。123- Tests cannot run from repo root (guard: `do-not-run-tests-from-root`); run from package dirs like `packages/opencode`.列表里的一个要点。
核心包提供:
test:Bun test,30 秒 timeout;test:ci:额外生成 JUnit;test:httpapi:coverage、auth、effect 三类 HTTP API exercise;bench:test与profile:test:性能调查入口。
见 packages/opencode/package.json:8-15。
packages/opencode/package.json
packages/opencode/package.json:8-15
8 "scripts": {项目脚本入口。9 "typecheck": "tsgo --noEmit",常用工程命令。10 "test": "bun test --timeout 30000",常用工程命令。11 "test:ci": "mkdir -p .artifacts/unit && bun test --timeout 30000 --reporter=junit --reporter-outfile=.artifacts/unit/junit.xml",12 "test:httpapi": "bun run script/httpapi-exercise.ts --mode coverage --fail-on-missing --fail-on-skip && bun run script/httpapi-exercise.ts --mode auth --fail-on-missing --fail-on-skip && bun run script/httpapi-exercise.ts --mode effect --fail-on-missing --fail-on-skip",13 "bench:test": "bun run script/bench-test-suite.ts",14 "profile:test": "bun run script/profile-test-files.ts",15 "build": "bun run script/build.ts",常用工程命令。
对于 session HTTP API 改动,focused test 与 test:httpapi 比“误跑根 test”更接近风险面。最终交付需要多大回归,仍由改动范围与项目要求决定。
第五步:理解 Turbo 会补哪些依赖
Section titled “第五步:理解 Turbo 会补哪些依赖”经 Turbo 执行 opencode#test 时,会先做 ^build,即上游 workspace 依赖构建,见 turbo.json:11-15。CI 版本也声明 JUnit output,并同样依赖上游 build,见 turbo.json:20-23。
turbo.json
turbo.json:11-15
11 "opencode#test": {12 "dependsOn": ["^build"],13 "outputs": [],14 "passThroughEnv": ["*"]15 },
turbo.json
turbo.json:20-23
20 "opencode#test:ci": {21 "dependsOn": ["^build"],22 "outputs": [".artifacts/unit/junit.xml"],23 "passThroughEnv": ["*"]
这解决了“核心测试引用了尚未构建的 workspace 包”。但如果开发者直接在 packages/opencode 手工运行 package test,就不能假设 Turbo 帮他做了这一步。
第六步:交付证据要分层报告
Section titled “第六步:交付证据要分层报告”可信报告应分别说:
声明检查:脚本/任务存在实际执行:命令、目录、退出码覆盖边界:哪些 package / 哪类路径没跑产物检查:generated diff / dist 是否符合预期“代码能 typecheck”不等于“HTTP 行为正确”;“单测通过”也不等于“SDK 已更新”。
7. workspace 与 catalog 解决什么
Section titled “7. workspace 与 catalog 解决什么”根 workspace 列出主要 package 集合;catalog 固定 effect、ai、TypeScript、UI 框架等共享版本,见 package.json:23-87。
package.json
package.json:23-87
23 "workspaces": {声明工作区范围。24 "packages": [25 "packages/*",26 "packages/console/*",27 "packages/sdk/js",28 "packages/slack"29 ],30 "catalog": {31 "@effect/opentelemetry": "4.0.0-beta.65",32 "@effect/platform-node": "4.0.0-beta.65",33 "@npmcli/arborist": "9.4.0",34 "@types/bun": "1.3.13",35 "@types/cross-spawn": "6.0.6",36 "@octokit/rest": "22.0.0",37 "@hono/zod-validator": "0.4.2",38 "@opentui/core": "0.2.14",39 "@opentui/keymap": "0.2.14",40 "@opentui/solid": "0.2.14",41 "ulid": "3.0.1",42 "@kobalte/core": "0.13.11",43 "@types/luxon": "3.7.1",44 "@types/node": "24.12.2",45 "@types/semver": "7.7.1",46 "@tsconfig/node22": "22.0.2",47 "@tsconfig/bun": "1.0.9",48 "@cloudflare/workers-types": "4.20251008.0",49 "@openauthjs/openauth": "0.0.0-20250322224806",50 "@pierre/diffs": "1.1.0-beta.18",51 "opentui-spinner": "0.0.6",52 "@solid-primitives/storage": "4.3.3",53 "@tailwindcss/vite": "4.1.11",54 "diff": "8.0.2",55 "dompurify": "3.3.1",56 "drizzle-kit": "1.0.0-beta.19-d95b7a4",57 "drizzle-orm": "1.0.0-beta.19-d95b7a4",58 "effect": "4.0.0-beta.65",59 "ai": "6.0.168",60 "cross-spawn": "7.0.6",61 "hono": "4.10.7",62 "hono-openapi": "1.1.2",63 "fuzzysort": "3.1.0",64 "luxon": "3.6.1",65 "marked": "17.0.1",66 "marked-shiki": "1.2.1",67 "remend": "1.3.0",68 "@playwright/test": "1.59.1",69 "semver": "7.7.4",70 "typescript": "5.8.2",71 "@typescript/native-preview": "7.0.0-dev.20251207.1",72 "zod": "4.1.8",73 "remeda": "2.26.0",74 "shiki": "3.20.0",75 "solid-list": "0.3.0",76 "tailwindcss": "4.1.11",77 "virtua": "0.49.1",78 "vite": "7.1.4",79 "@solidjs/meta": "0.29.4",80 "@solidjs/router": "0.15.4",81 "@solidjs/start": "https://pkg.pr.new/@solidjs/start@dfb2020",82 "@sentry/solid": "10.36.0",83 "@sentry/vite-plugin": "4.6.0",84 "solid-js": "1.9.10",85 "vite-plugin-solid": "2.11.10",86 "@lydell/node-pty": "1.2.0-beta.10"87 }
这类似 dependency management:子 package 可以使用 catalog:,减少同库多版本漂移。代价是根 catalog 变更影响面很大,应视为跨包升级,而不是单包小改。
根 tsconfig.json 只继承 Bun 基线,没有集中塞入大量 compiler option,见 tsconfig.json:1-5。这反过来强调 package 自己的 typecheck 配置与 script 才是执行边界。
8. task graph 的关键选择
Section titled “8. task graph 的关键选择”turbo.json 展示了三类配置:
typecheck没声明跨包依赖;build产出dist/**;- 特定 test task 依赖
^build,CI task还记录 JUnit output。
见 turbo.json:5-42。
turbo.json
turbo.json:5-42
5 "tasks": {6 "typecheck": {},常用工程命令。7 "build": {常用工程命令。8 "dependsOn": [],9 "outputs": ["dist/**"]10 },11 "opencode#test": {12 "dependsOn": ["^build"],13 "outputs": [],14 "passThroughEnv": ["*"]15 },16 "test:ci": {17 "outputs": [".artifacts/unit/junit.xml"],18 "passThroughEnv": ["*"]19 },20 "opencode#test:ci": {21 "dependsOn": ["^build"],22 "outputs": [".artifacts/unit/junit.xml"],23 "passThroughEnv": ["*"]24 },25 "@opencode-ai/app#test": {26 "dependsOn": ["^build"],27 "outputs": []28 },29 "@opencode-ai/app#test:ci": {30 "dependsOn": ["^build"],31 "outputs": [".artifacts/unit/junit.xml"],32 "passThroughEnv": ["*"]33 },34 "@opencode-ai/ui#test": {35 "dependsOn": ["^build"],36 "outputs": []37 },38 "@opencode-ai/ui#test:ci": {39 "dependsOn": ["^build"],40 "outputs": [".artifacts/unit/junit.xml"],41 "passThroughEnv": ["*"]42 }
为什么不让所有任务一律 dependsOn: ["^build"]?源码没有写解释。合理的设计解释是:不同任务对上游构建产物的依赖不同,过度串行会拖慢反馈。这个“为什么”是推断,不是代码明示。
9. 运行时包的跨环境边界
Section titled “9. 运行时包的跨环境边界”packages/opencode/package.json 不只声明脚本,还通过 imports 为 #db、#pty 选择 Bun、Node 与 default 实现,见 packages/opencode/package.json:21-37。
packages/opencode/package.json
packages/opencode/package.json:21-37
21 "bin": {22 "opencode": "./bin/opencode"23 },24 "exports": {包对外暴露入口。25 "./*": "./src/*.ts"26 },27 "imports": {28 "#db": {29 "bun": "./src/storage/db.bun.ts",30 "node": "./src/storage/db.node.ts",31 "default": "./src/storage/db.bun.ts"32 },33 "#pty": {34 "bun": "./src/pty/pty.bun.ts",35 "node": "./src/pty/pty.node.ts",36 "default": "./src/pty/pty.bun.ts"37 }
这提醒测试设计:类型通过不代表所有运行环境都通过。涉及数据库或 PTY 的改动,应按条件导入覆盖实际目标 runtime。
10. OpenCode 的选择
Section titled “10. OpenCode 的选择”| 选择 | 好处 | 代价 |
|---|---|---|
| 禁止 root test | 强迫开发者选择真实 package 边界 | 初学者会觉得入口不直观 |
| catalog 统一版本 | 降低 workspace 依赖漂移 | 升级影响面集中 |
| package scripts 封装工具 | 命令与参数可演进 | 不能凭通用生态习惯直接跑 tsc |
| Turbo 只声明必要依赖 | 反馈更快、缓存更准确 | task graph 需要持续维护 |
| SDK 生成作为 build | 契约变化可机械传播 | 生成物与源码必须一起审查 |
11. 可以带走的方法
Section titled “11. 可以带走的方法”方法一:让测试入口匹配所有权边界
Section titled “方法一:让测试入口匹配所有权边界”根目录负责发现与编排,package 负责具体命令。不要设计一个含义模糊的“全测”按钮。
验证问题:失败时,开发者能否立刻知道是哪个 package、哪类检查?
方法二:把生成代码当交付链的一等公民
Section titled “方法二:把生成代码当交付链的一等公民”contract 变化要能触发生成、diff、typecheck,而不是靠发布前想起来。
验证问题:CI 能否发现 server schema 已变、SDK 生成物没更新?
方法三:按风险建立验证阶梯
Section titled “方法三:按风险建立验证阶梯”静态检查快、focused test 定位准、跨包 build 捕获集成问题、E2E 验证用户路径。交付时报告实际跑到哪一级。
验证问题:一个 permission 行为变更为什么不能只靠 typecheck?
12. 费曼复述与练习
Section titled “12. 费曼复述与练习”请回答:
- 为什么根目录可以 typecheck,却故意不能 test?
- package script 与 Turbo task 各负责什么?
- “配置了 test:httpapi”和“本次 test:httpapi 通过”有什么区别?
练习阶梯:
-
入门:为 root、opencode、SDK 三层各写一句职责。
-
进阶:给“新增一个 session endpoint”列出按速度排序的验证阶梯。
-
源码追踪:从
turbo.json:11解释^build对直接运行 package test 有何边界。turbo.json
turbo.json:1111 "opencode#test": { -
迁移:为 mini agent 设计 root guard,防止开发者在错误目录误跑测试。
最后复盘:工程化是可执行的信任链
Section titled “最后复盘:工程化是可执行的信任链”文件改动 -> package ownership -> package typecheck / focused test -> task graph 补齐上游 -> generated/build 产物 -> 清楚报告已验证与未验证边界到这里,我们已经读完 runtime 的关键模块与支撑它的工程边界。最后一章不再继续堆 OpenCode 细节,而是做一次删减实验:哪些机制必须保留,才能亲手做出一个小而真的 coding agent?