主题
OpenSpec 规格治理与测试策略
本章目标:
- 弄清
openspec/怎么用specs/(真源)+changes/(propose → apply → archive 生命周期)管理 a-cdm 的规格,以及规格如何回指代码- 看懂 acdm-backend 的 pytest 双层测试(unit 无 DB / integration
@db真 Postgres)如何映射到 CI 两个 job- 理解 deer-flow 前端 vitest 纯函数测试为何只测
core/不进 CI,以及test/eval模型评测在工程里扮演什么角色
TL;DR
a-cdm 的"需求真相"不在散落的 docs/,而在 openspec/:specs/<capability>/spec.md 是 21 个能力域的 living 规格(用 Requirement + Scenario 的 SHALL/WHEN/THEN 句式写),任何改动先在 changes/<name>/ 起一个含 proposal.md/specs/<cap>/spec.md delta/tasks.md/design.md 的 change(propose),实施时打勾 tasks(apply),上线稳定后 openspec archive 把 delta 合并回 spec 真源并搬进 changes/archive/(75 个已归档)。测试侧分三层:acdm-backend 的 pytest 按 @pytest.mark.db 切成 backend:test(无 DB,跳 39 个 db 测试)和 backend:test-db(真 Postgres + alembic 建表)两个 CI job;deer-flow 前端 30 个 vitest 测试只覆盖 core/ 纯函数、本地手跑、CI 只跑 pnpm check(eslint + tsc);test/eval 是离线模型选型评测(产出 REPORT.md),不进 CI。
Overview(为什么需要 OpenSpec 而不是写在 docs 里)
a-cdm 是一个被深度二开的 deer-flow,跨 acdm-backend / deer-flow 引擎 / 前端 / Caddy / CI 五个面,半年里做了上百次结构性变更(Keycloak SSO、Phase 2 授权层、切 Aegra、HTTPS canonical host……)。如果需求和决策都写进自由格式的 docs/*.md,会立刻 drift —— 项目 CLAUDE.md 开篇就警告"可变状态查命令,不在文档维护(否则必 drift)",而且方法论里专门记了一条教训:项目自带的 .md 文档本身可能有错。
OpenSpec 解决的问题是:把"需求是什么"与"这次改了什么"分离,并让规格可被工具校验。
specs/回答"现在系统应该满足什么"(living truth,只反映已上线状态)changes/回答"我提议改成什么、为什么、怎么改、改完打勾没"(生命周期工件)- change archive 后,delta 合并回 spec —— spec 永远等于"已上线现状",历史决策冻结在 archive 里可回溯
这跟散文档的根本差别是:每个 Requirement 带可被第三方验证的 Scenario(WHEN ... THEN ...),openspec validate <change> --strict 能机器校验结构完整性(docs/runbooks/contribution.md:75),Conventional Commits 有专门的 archive / translate type 对应 OpenSpec 操作(docs/runbooks/contribution.md:62-63)。
Architecture:openspec/ 目录的两个支柱
openspec/ 只有两个子目录,对应"真源"与"生命周期":
| 目录 | 角色 | 内容形态 | 规模 | Source |
|---|---|---|---|---|
openspec/specs/<capability>/spec.md | living 真源 | 每文件一个 capability,## Requirements 下若干 ### Requirement + #### Scenario | 21 个 capability,合计 ~9980 行 | openspec/specs/architecture/spec.md:1-40 |
openspec/changes/<name>/ | 进行中的变更 | proposal.md + specs/<cap>/spec.md(delta)+ tasks.md + design.md | 29 个进行中 | openspec/changes/aegra-server-migration/proposal.md:22-51 |
openspec/changes/archive/<dated-name>/ | 已归档变更 | 同上 4 件套 + .openspec.yaml | 75 个已归档 | openspec/changes/archive/2026-04-21-monorepo-flip/.openspec.yaml:1-2 |
21 个 capability 不是平铺的 —— architecture 是顶层规格,其 Purpose 明确声明它"作为所有其他 capability 的基础",并把专项内容分派下去(16 位专家矩阵 → project-workspace、知识层 → llm-wiki、团队 RACI / 术语表 → docs/runbooks/,见 openspec/specs/architecture/spec.md:7-19)。规格规模差异极大:meeting-calendar 1641 行(会议域最复杂),db-retention 仅 69 行。
Components / Subsystems
spec.md:Requirement + Scenario 的契约语法
每个 capability 的 spec.md 用受控自然语言写,核心是两级结构:### Requirement 用 SHALL/MUST 描述系统义务,下挂 #### Scenario 用 WHEN/THEN/AND 写可验证用例。
openspec/specs/project-management/spec.md:6-46 是个典型:Requirement "ProjectStorage facade 封装项目级存储边界"用 5 条 MUST 列出 ProjectStorage 类必须做什么(构造接收 project_name、暴露 project_root_key、_assert_under_root 三层守护……),且直接回指实现文件 acdm-backend/app/storage/project_storage.py;紧跟 4 个 Scenario 把"越界 key 拦截""路径穿越拦截""非法项目名拦截""删除整个项目根"写成 WHEN ... THEN ...。这种"规格里写死实现文件路径"的写法,使 spec 既是需求又是代码地图 —— auth/spec.md 引了 5 处 acdm-backend/app 或 deer-flow/backend 路径,project-management/spec.md 2 处。
新建出来的 capability spec 会有占位 Purpose:## Purpose\nTBD - created by archiving change <name>. Update Purpose after archive.(openspec/specs/project-management/spec.md:3-4),说明 capability 的 spec.md 本身是由"第一个 seed change archive"时生成的。
change 四件套:proposal / spec delta / tasks / design
一个进行中的 change(以 aegra-server-migration 为例)目录恒为:
| 文件 | 职责 | 关键段 | Source |
|---|---|---|---|
proposal.md | 为什么改 + 改什么 + 影响 | ## Why / ## What Changes / ## Capabilities(New/Modified)/ ## Impact | openspec/changes/aegra-server-migration/proposal.md:1-77 |
specs/<cap>/spec.md | 规格 delta,不是全文 | ## MODIFIED Requirements / ## ADDED Requirements / ## REMOVED | openspec/changes/aegra-server-migration/specs/architecture/spec.md:1-60 |
tasks.md | MR 拆分 + 勾选清单 + 状态表 | ## 状态 表 + ## MR1/MR2... 下 - [x]/[ ] | openspec/changes/aegra-server-migration/tasks.md:1-40 |
design.md | 设计上下文与决策 | ## Context / ## Goals/Non-Goals / 决策 D1..Dn / Open Question | openspec/changes/aegra-server-migration/design.md:1-45 |
关键点是 spec delta 只写增量:changes/aegra-server-migration/specs/architecture/spec.md 顶部就是 ## MODIFIED Requirements 后跟整段重写的 "端口分配与服务拓扑",再 ## ADDED Requirements 加 "Agent Runtime Server 持久化与双层隔离"(openspec/changes/aegra-server-migration/specs/architecture/spec.md:1-60)。archive 时这些 MODIFIED/ADDED 块会被合并进 specs/architecture/spec.md 真源,REMOVED 块删掉对应 Requirement。
proposal.md 的 ## Capabilities 段是机器可读的索引,显式列 ### New Capabilities / ### Modified Capabilities,逐条标 MODIFIED/ADDED + Requirement 名(openspec/changes/aegra-server-migration/proposal.md:36-51),openspec validate 据此核对 delta 与声明一致。
tasks.md:把规格落到 MR 与回滚
tasks.md 是规格与工程的接缝。aegra-server-migration/tasks.md 顶部一句 "拆 4 个独立 MR,每 MR 独立可合 + 可回滚",再用状态表记录每个 MR 的合并 commit(MR1 ✅ 合 main(!81))、推迟项(MR3 ⏸️ 推迟)、剩余待办,然后 ## MR1 下是逐条 - [x] 清单(openspec/changes/aegra-server-migration/tasks.md:1-40)。这套写法把"规格 → 可回滚的部署单元"显式化,与项目 CLAUDE.md 铁律"禁在 main 直接 commit、永远走 MR"对齐。
Data Flow:一个 change 从提案到归档
流程要点:
openspec new change <name>(docs/runbooks/contribution.md:155)生成空骨架。- 写
proposal.md+specs/<cap>/spec.md(delta)+tasks.md+design.md(docs/runbooks/contribution.md:156)。 openspec validate <name> --type change --strict必须绿才进 review(docs/runbooks/contribution.md:157,也是 PR 自检清单一项docs/runbooks/contribution.md:75)。- 按
tasks.md拆 MR,经 CI 合 main。 - 上线稳定后
openspec archive <name>(docs/runbooks/contribution.md:159):delta 合回 spec 真源,change 搬进changes/archive/并加日期前缀(75 个已归档全部^\d{4}-前缀,如2026-04-21-monorepo-flip)。
注意一个现实约束:tasks.md 允许"部分待办留给独立 MR / 其他 change"再 archive ——aegra-server-migration/tasks.md 的 MR3(checkpointer cleanup)被推迟,§4.3 留给 admin-chat-view change,不阻塞本 change archive(openspec/changes/aegra-server-migration/tasks.md:24-30)。archive 判据是"功能上线 + 稳定",不是"所有勾打满"。
速查表:21 个 capability spec
| capability | 行数 | 主题(从 Purpose / Requirement 读) | Source |
|---|---|---|---|
architecture | 899 | 顶层 7 层架构 + 端口 + 二开策略,所有 capability 的基础 | openspec/specs/architecture/spec.md:1 |
meeting-calendar | 1641 | 会议日历 / 子日历 / 冲突检测(最大) | openspec/specs/meeting-calendar/spec.md:1 |
meeting-insight-engine | 1176 | 会议洞察引擎面板与聚合 | openspec/specs/meeting-insight-engine/spec.md:1 |
llm-wiki | 1066 | LLM 知识仓库 / Wiki 协编 | openspec/specs/llm-wiki/spec.md:1 |
project-workspace | 1003 | 项目工作区 + 16 专家矩阵 | openspec/specs/project-workspace/spec.md:1 |
auth | 963 | Keycloak SSO + 授权层 + 公网边界加固 | openspec/specs/auth/spec.md:1 |
meeting-reports | 698 | BA 专家报告工作流 | openspec/specs/meeting-reports/spec.md:1 |
project-agent-platform | 413 | agent-as-colleague memory substrate | openspec/specs/project-agent-platform/spec.md:1 |
weekly-report | 371 | 周报生成 | openspec/specs/weekly-report/spec.md:1 |
ai-reversible-actions | 255 | 可逆 DB 操作 | openspec/specs/ai-reversible-actions/spec.md:1 |
knowledge-sources | 198 | 知识源 MCP Gateway | openspec/specs/knowledge-sources/spec.md:1 |
project-management | 184 | ProjectStorage / 项目改名原子迁移 | openspec/specs/project-management/spec.md:1 |
ai-task-lifecycle | 171 | AI 任务生命周期 | openspec/specs/ai-task-lifecycle/spec.md:1 |
roadmap | 162 | Phase 4 要素 + Inversion Reflex | openspec/specs/roadmap/spec.md:1 |
chat-embed | 142 | 嵌入式 chat | openspec/specs/chat-embed/spec.md:1 |
erp-analyzer | 132 | ERP 录屏分析 | openspec/specs/erp-analyzer/spec.md:1 |
frontend-architecture | 130 | 前端架构规约 | openspec/specs/frontend-architecture/spec.md:1 |
admin-trash | 124 | 管理员回收站 | openspec/specs/admin-trash/spec.md:1 |
chat-share | 108 | 对话分享 | openspec/specs/chat-share/spec.md:1 |
thread-fork-sharing | 75 | thread fork / 分享 | openspec/specs/thread-fork-sharing/spec.md:1 |
db-retention | 69 | DB 软删保留策略(最小) | openspec/specs/db-retention/spec.md:1 |
测试策略:三层互不重叠
a-cdm 的测试不是一锅端,而是按"被测对象 + 依赖代价"切成三层,每层有不同的执行场所与触发条件:
acdm-backend pytest:db marker 切两层
pytest.ini 定义了关键的 db marker,描述就写明了它的双 job 语义:db: integration-level test needing a real Postgres (skipped by -m "not db" in backend:test CI job; run by backend:test-db job with Postgres service)(acdm-backend/pytest.ini:7)。配置还设了 asyncio_mode = auto(全异步)和 --strict-markers(用未声明 marker 直接报错)。
测试目录三分:tests/unit/(83 个文件,mock 一切外部)、tests/integration/(19 个文件,走真实 route 层)、tests/fixtures/。整个 tests/ 里 39 个文件带 @pytest.mark.db。
集成测试的巧妙之处在 tests/integration/conftest.py 的 client fixture —— 它故意不用 with TestClient(app) as c:注释说明那样会触发 FastAPI lifespan,而 lifespan 里 recover_pending_on_startup / probe_streaming_roles 会试图连真 PG / 真 LLM,在无 DB 的 backend:test job 里炸 OSError(acdm-backend/tests/integration/conftest.py:31-42)。改用 dependency_overrides 接管 get_current_user(login_as)+ get_db(mock_db 返回 MagicMock AsyncSession),让 route 层走真实逻辑但 DB 是假的。make_user / set_project 等 helper 把"以谁身份登录、db.get(Project) 返回什么"参数化(acdm-backend/tests/integration/conftest.py:22-78)。这就是 test_phase2_authz.py 能在无 DB 的 CI 里测 4 角色权限分支的原因(acdm-backend/tests/integration/test_phase2_authz.py:1-35)。
全局 env baseline 由 tests/conftest.py 用 os.environ.setdefault 灌 dummy 值(DATABASE_URL/SECRET_KEY/KEYCLOAK_CLIENT_SECRET/JOINTPILOT_API_KEY),解决"Settings import 时 required field 缺失导致 collection 失败"。它还干了一件特殊事:用 importlib 显式 load deer-flow 的 deerflow.agents.memory.storage 等几个真实文件并注册成 namespace package,让 acdm-backend 的 isinstance 校验能拿到 deer-flow 真 class,又不触发 deerflow/agents/__init__.py 的整链 middleware 副作用(acdm-backend/tests/conftest.py:50-95)。
CI 两个 job:test 与 test-db
| job | 命令 | 依赖 | 作用 | Source |
|---|---|---|---|---|
backend:test | pytest -q -m "not db" --ignore=...8 个文件 | dummy env,无 DB | 跑 unit + 不需 PG 的 integration | .gitlab-ci.yml:156-183 |
backend:test-db | pytest -q -m "db" | 真 Postgres service + alembic 建表 | 跑 @db 集成测试,作 on_success gate 挡 MR | .gitlab-ci.yml:185-233 |
backend:test 用 -m "not db" 跳过 39 个 @db 测试,另外用 8 个 --ignore= 跳过一批"fixture monkeypatch 在 CI Linux+Python 3.11+pydantic-settings v2.6 环境下不生效"的文件(如 test_mcp_tools_wiki.py / test_gitlab_client.py),CI 注释明说这些测试本地都过、跳过只是 CI 暂缺覆盖(.gitlab-ci.yml:168-183)。backend:test-db 带真 Postgres、用 alembic 建表,挂在主链路作 gate —— @db 测试红会直接挡 MR(.gitlab-ci.yml:185-233)。.backend-base 的 rules 用 compare_to: refs/heads/main 固定 diff 基线,避免 feature branch 首次 push 时 CI_COMMIT_BEFORE_SHA=0000 把全量文件误判 changed(.gitlab-ci.yml:51-60)。
deer-flow 前端 vitest:只测 core/ 纯函数,不进 CI
vitest.config.ts 只 include tests/unit/**/*.test.ts,别名 @ → src(deer-flow/frontend/vitest.config.ts:1-15)。30 个测试文件全部针对 core/ 或组件里的纯函数 / 纯逻辑:calendar-filter.test.ts 测合并规则纯函数 computeNextCheckedIds(deer-flow/frontend/tests/unit/core/acdm/calendar-filter.test.ts:1-40)、sse-parser.test.ts 测洞察引擎 SSE 解析、screenshot-dedup.test.ts 测截图去重 —— 全是 a-cdm 定制逻辑,不测 React 渲染。
关键事实:前端 CI 不跑 vitest。frontend:check job 的 script 只有 pnpm check,即 eslint . --ext .ts,.tsx && tsc --noEmit(deer-flow/frontend/package.json:9,.gitlab-ci.yml:264)。pnpm test(vitest run)和 pnpm test:e2e(playwright,deer-flow/frontend/e2e/meeting-reports.spec.ts)都是本地 / 手动执行。设计取舍是:前端逻辑测试覆盖窄(纯函数),CI 用类型检查 + lint 守住大面,把 vitest 留作本地快速回归 —— 跨架构 build 前端在 CI 已知有坑(项目 CLAUDE.md 红线"不用 QEMU 跨架构 build deer-flow 前端"),CI 不适合跑重前端流程。
test/eval:离线模型选型,不是回归测试
test/eval 与 test/eval-v2 不是单元/集成测试,是模型选型评测:固定一个真实场景(112 项目 7 份会议纪要 20K 汉字一次性生成周报),对多个候选模型跑同一 prompt,用客观打分脚本 + 人工 ground truth 评分,产出选型结论。test/eval/REPORT.md 的 TL;DR 直接给生产建议:"没有任何火山方舟 Coding Plan 模型达到 Claude 直吃效果(最佳 73.9 vs baseline 96.9)","主力 doubao-seed-2.0-pro + thinking disabled"(test/eval/REPORT.md:1-40)。
test/eval-v2/scripts/score_objective_v2.py 是打分器,6 维加权:structure 20 / recall 25 / name 15 / term 15 / source 10 / format 15,召回率分母换成 38 条加权洞察的人工标准答案、人名分母是 30 个真实人名(test/eval-v2/scripts/score_objective_v2.py:1-25),输出 _scores_objective_v2.csv 排名表(test/eval-v2/outputs/_scores_objective_v2.csv)。这层不进 CI、不阻塞合并 —— 它服务的是"选哪个模型上生产"的工程决策,对应 docs/runbooks/model-selection.md。
Configuration
| Config | 位置 | 含义 | Source |
|---|---|---|---|
asyncio_mode = auto | acdm-backend/pytest.ini:3 | 所有 async 测试自动 await,无需 @pytest.mark.asyncio | acdm-backend/pytest.ini:3 |
--strict-markers | acdm-backend/pytest.ini:5 | 用未声明 marker 直接报错(防 typo 静默跳过) | acdm-backend/pytest.ini:5 |
marker db | acdm-backend/pytest.ini:7 | 标记需真 Postgres 的测试,CI 据此分 job | acdm-backend/pytest.ini:7 |
marker payload_passthrough | acdm-backend/pytest.ini:8-9 | test_llm_client fixture 开关:透传不删 temperature | acdm-backend/pytest.ini:8-9 |
testpaths = tests | acdm-backend/pytest.ini:4 | 只从 tests/ 收集 | acdm-backend/pytest.ini:4 |
vitest include | deer-flow/frontend/vitest.config.ts:13 | 只跑 tests/unit/**/*.test.ts | deer-flow/frontend/vitest.config.ts:13 |
CI compare_to: refs/heads/main | .gitlab-ci.yml:51-53 | 固定 diff 基线,防 feature branch 首推误判全量 changed | .gitlab-ci.yml:51-53 |
openspec/**/* 命中 docs:ok | .gitlab-ci.yml:354 | 改 openspec 走 docs-only 占位 job,无自动 openspec validate | .gitlab-ci.yml:330-358 |
扩展指南
加一个新 capability / 改一个现有 capability
bash
openspec new change my-feature
# 编辑生成的骨架:
# openspec/changes/my-feature/proposal.md # Why / What Changes / ## Capabilities(New|Modified)/ Impact
# openspec/changes/my-feature/specs/<cap>/spec.md # 只写 ## ADDED|MODIFIED|REMOVED Requirements(delta!)
# openspec/changes/my-feature/tasks.md # ## MR1.. + - [ ] 勾选清单
# openspec/changes/my-feature/design.md # ## Context / Goals / 决策 D1.. / Open Question
openspec validate my-feature --type change --strict # 必须绿
# 实施完成 + 上线稳定后:
openspec archive my-feature # delta 合回 specs/,搬进 changes/archive/约束(从仓库规约 / 代码读出):
- spec delta 只写增量:用
## ADDED Requirements/## MODIFIED Requirements/## REMOVED Requirements,不要贴整篇 spec(openspec/changes/aegra-server-migration/specs/architecture/spec.md:1)。 - 每个 Requirement 必须有至少一个
#### Scenario(WHEN/THEN),openspec validate --strict会校验;新 capability 至少要 archive 一个 seed change 才会生成specs/<cap>/spec.md。 proposal.md的## Capabilities段声明的 ADDED/MODIFIED 名必须与 spec delta 一致(openspec/changes/aegra-server-migration/proposal.md:36-51)。- 不在
docs/specs/或docs/plans/新建文档(项目红线 +docs/runbooks/contribution.md:162),真相只进openspec/specs/。 - CI 不自动跑
openspec validate——openspec/**/*命中docs:ok占位 job(.gitlab-ci.yml:354),validate 是开发者本地 + PR 自检责任(docs/runbooks/contribution.md:75)。
加一个 acdm-backend 测试
python
# tests/unit/test_xxx.py — 纯逻辑,不连 DB,backend:test 跑
async def test_pure_logic():
assert my_func(...) == expected
# tests/integration/test_xxx.py — 走 route 层,无真 DB
import pytest
from tests.integration.conftest import login_as, make_user, set_project
def test_route(client, mock_db):
login_as(make_user(sub="alice", is_admin=False))
set_project(mock_db, "p1")
resp = client.get("/api/acdm/projects/p1")
assert resp.status_code == 400 # BusinessException → 400 + {code,data,message}
# 需要真 Postgres 的集成测试 → 打 db marker,只在 backend:test-db 跑
pytestmark = pytest.mark.db约束:
--strict-markers生效,用@pytest.mark.db前它必须在pytest.ini已声明(已声明,直接用)。- 集成测试用
clientfixture(不带with,见 conftest 注释)避免触发 lifespan 连真 PG(acdm-backend/tests/integration/conftest.py:31-42)。 BusinessException经全局 handler 变成 HTTP 400 + body{code, data, message},断言用status_code == 400+resp.json()["code"](acdm-backend/tests/integration/test_phase2_authz.py:8-12)。- 前端测试只写纯函数/纯逻辑、放
tests/unit/**/*.test.ts、记得本地pnpm test(CI 不跑)。
Common Pitfalls / 实战 Tips
- spec.md 不是改了就生效:
specs/<cap>/spec.md是真源,但日常改动不该直接编辑它,要走 change 的 delta + archive 合并;直接改真源会绕过校验、丢历史。 - archive ≠ 全部打勾:
tasks.md可留待办给独立 MR / 其他 change,archive 判据是"功能上线 + 稳定"(openspec/changes/aegra-server-migration/tasks.md:24-30)。 backend:test绿不代表全测过:它跳了 39 个@db+ 8 个--ignore文件,@db测试由backend:test-db兜底挡 MR;别只看backend:test(.gitlab-ci.yml:168-183)。- 前端 vitest 不在 CI:改
core/纯函数后 CI 只跑 eslint+tsc,逻辑回归得本地pnpm test(.gitlab-ci.yml:264)。 - 集成测试别用
with TestClient(app):会触发 lifespan 连真 PG/LLM,在无 DB 的backend:test炸 OSError —— 直接TestClient(app)+ dependency_overrides(acdm-backend/tests/integration/conftest.py:31-42)。 - test/eval 数字会过期:
REPORT.md是某次评测快照(2026-04-19),模型 / prompt 变了结论就失效,选型现状以docs/runbooks/model-selection.md+ 当前 ai-roles-registry 为准。
References
openspec/specs/architecture/spec.md:1-40— 顶层 capability,声明它是所有 capability 的基础 + 分派关系openspec/specs/project-management/spec.md:1-46— Requirement+Scenario 语法样例,规格回指实现文件openspec/changes/aegra-server-migration/proposal.md:1-77— change proposal 四段结构(Why/What/Capabilities/Impact)openspec/changes/aegra-server-migration/specs/architecture/spec.md:1-60— spec delta(MODIFIED/ADDED Requirements)写法openspec/changes/aegra-server-migration/tasks.md:1-40— tasks.md MR 拆分 + 状态表 + 勾选清单docs/runbooks/contribution.md:62-162— OpenSpec 工作流命令 + Conventional Commits 的 archive/translate type + PR 自检acdm-backend/pytest.ini:1-10— pytest 配置:asyncio auto / strict-markers / db marker 双 job 语义acdm-backend/tests/conftest.py:1-95— 全局 env baseline + importlib 注入 deerflow 真 classacdm-backend/tests/integration/conftest.py:1-78— 集成测试 dependency_overrides fixture(避 lifespan).gitlab-ci.yml:156-233— backend:test / backend:test-db 双 job;frontend:check 只跑 pnpm checkdeer-flow/frontend/vitest.config.ts:1-15— 前端 vitest 只测 tests/unittest/eval/REPORT.md:1-40+test/eval-v2/scripts/score_objective_v2.py:1-25— 离线模型评测与 6 维打分
Related Pages
| Page | Relationship |
|---|---|
| 系统整体架构 | 本章 architecture capability 是该章架构规格的真源 |
| CICD与生产部署运维 | 本章 backend:test/test-db/frontend:check job 是该章 CI 流水线一部分 |
| 仓库结构与代码地图 | 本章解释该章列出的 openspec/ 与 test/ 目录用途 |
| AI角色注册表与模型矩阵 | 本章 test/eval 评测结论喂给该章模型选型 |
| Aegra运行时与LangGraph | 本章用 aegra-server-migration change 作规格生命周期范例,该章讲其实现 |
| 项目管理与资源授权 | 本章引 project-management spec / phase2_authz 集成测试,该章讲其实现 |