Skip to content

OpenSpec 规格治理与测试策略

本章目标:

  1. 弄清 openspec/ 怎么用 specs/(真源)+ changes/(propose → apply → archive 生命周期)管理 a-cdm 的规格,以及规格如何回指代码
  2. 看懂 acdm-backend 的 pytest 双层测试(unit 无 DB / integration @db 真 Postgres)如何映射到 CI 两个 job
  3. 理解 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.mdliving 真源每文件一个 capability,## Requirements 下若干 ### Requirement + #### Scenario21 个 capability,合计 ~9980 行openspec/specs/architecture/spec.md:1-40
openspec/changes/<name>/进行中的变更proposal.md + specs/<cap>/spec.md(delta)+ tasks.md + design.md29 个进行中openspec/changes/aegra-server-migration/proposal.md:22-51
openspec/changes/archive/<dated-name>/已归档变更同上 4 件套 + .openspec.yaml75 个已归档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 用受控自然语言写,核心是两级结构:### RequirementSHALL/MUST 描述系统义务,下挂 #### ScenarioWHEN/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/appdeer-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)/ ## Impactopenspec/changes/aegra-server-migration/proposal.md:1-77
specs/<cap>/spec.md规格 delta,不是全文## MODIFIED Requirements / ## ADDED Requirements / ## REMOVEDopenspec/changes/aegra-server-migration/specs/architecture/spec.md:1-60
tasks.mdMR 拆分 + 勾选清单 + 状态表## 状态 表 + ## MR1/MR2...- [x]/[ ]openspec/changes/aegra-server-migration/tasks.md:1-40
design.md设计上下文与决策## Context / ## Goals/Non-Goals / 决策 D1..Dn / Open Questionopenspec/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 从提案到归档

流程要点:

  1. openspec new change <name>(docs/runbooks/contribution.md:155)生成空骨架。
  2. proposal.md + specs/<cap>/spec.md(delta)+ tasks.md + design.md(docs/runbooks/contribution.md:156)。
  3. openspec validate <name> --type change --strict 必须绿才进 review(docs/runbooks/contribution.md:157,也是 PR 自检清单一项 docs/runbooks/contribution.md:75)。
  4. tasks.md 拆 MR,经 CI 合 main。
  5. 上线稳定后 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
architecture899顶层 7 层架构 + 端口 + 二开策略,所有 capability 的基础openspec/specs/architecture/spec.md:1
meeting-calendar1641会议日历 / 子日历 / 冲突检测(最大)openspec/specs/meeting-calendar/spec.md:1
meeting-insight-engine1176会议洞察引擎面板与聚合openspec/specs/meeting-insight-engine/spec.md:1
llm-wiki1066LLM 知识仓库 / Wiki 协编openspec/specs/llm-wiki/spec.md:1
project-workspace1003项目工作区 + 16 专家矩阵openspec/specs/project-workspace/spec.md:1
auth963Keycloak SSO + 授权层 + 公网边界加固openspec/specs/auth/spec.md:1
meeting-reports698BA 专家报告工作流openspec/specs/meeting-reports/spec.md:1
project-agent-platform413agent-as-colleague memory substrateopenspec/specs/project-agent-platform/spec.md:1
weekly-report371周报生成openspec/specs/weekly-report/spec.md:1
ai-reversible-actions255可逆 DB 操作openspec/specs/ai-reversible-actions/spec.md:1
knowledge-sources198知识源 MCP Gatewayopenspec/specs/knowledge-sources/spec.md:1
project-management184ProjectStorage / 项目改名原子迁移openspec/specs/project-management/spec.md:1
ai-task-lifecycle171AI 任务生命周期openspec/specs/ai-task-lifecycle/spec.md:1
roadmap162Phase 4 要素 + Inversion Reflexopenspec/specs/roadmap/spec.md:1
chat-embed142嵌入式 chatopenspec/specs/chat-embed/spec.md:1
erp-analyzer132ERP 录屏分析openspec/specs/erp-analyzer/spec.md:1
frontend-architecture130前端架构规约openspec/specs/frontend-architecture/spec.md:1
admin-trash124管理员回收站openspec/specs/admin-trash/spec.md:1
chat-share108对话分享openspec/specs/chat-share/spec.md:1
thread-fork-sharing75thread fork / 分享openspec/specs/thread-fork-sharing/spec.md:1
db-retention69DB 软删保留策略(最小)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.pyclient 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.pyos.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:testpytest -q -m "not db" --ignore=...8 个文件dummy env,无 DB跑 unit + 不需 PG 的 integration.gitlab-ci.yml:156-183
backend:test-dbpytest -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-baserulescompare_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 不跑 vitestfrontend: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/evaltest/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 = autoacdm-backend/pytest.ini:3所有 async 测试自动 await,无需 @pytest.mark.asyncioacdm-backend/pytest.ini:3
--strict-markersacdm-backend/pytest.ini:5用未声明 marker 直接报错(防 typo 静默跳过)acdm-backend/pytest.ini:5
marker dbacdm-backend/pytest.ini:7标记需真 Postgres 的测试,CI 据此分 jobacdm-backend/pytest.ini:7
marker payload_passthroughacdm-backend/pytest.ini:8-9test_llm_client fixture 开关:透传不删 temperatureacdm-backend/pytest.ini:8-9
testpaths = testsacdm-backend/pytest.ini:4只从 tests/ 收集acdm-backend/pytest.ini:4
vitest includedeer-flow/frontend/vitest.config.ts:13只跑 tests/unit/**/*.test.tsdeer-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 已声明(已声明,直接用)。
  • 集成测试用 client fixture(不带 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 真 class
  • acdm-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 check
  • deer-flow/frontend/vitest.config.ts:1-15 — 前端 vitest 只测 tests/unit
  • test/eval/REPORT.md:1-40 + test/eval-v2/scripts/score_objective_v2.py:1-25 — 离线模型评测与 6 维打分
PageRelationship
系统整体架构本章 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 集成测试,该章讲其实现

公司内部参考 · 由 claude-wiki-gen 基于源码自动生成的二次分析